hash.c revision 5dfecf96
1/* 2 * Copyright (c) 2002 by The XFree86 Project, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 19 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 * SOFTWARE. 21 * 22 * Except as contained in this notice, the name of the XFree86 Project shall 23 * not be used in advertising or otherwise to promote the sale, use or other 24 * dealings in this Software without prior written authorization from the 25 * XFree86 Project. 26 * 27 * Author: Paulo César Pereira de Andrade 28 */ 29 30/* $XFree86: xc/programs/xedit/lisp/hash.c,v 1.5 2003/04/27 18:17:32 tsi Exp $ */ 31 32#include "lisp/hash.h" 33 34/* A simple hash-table implementation 35 * TODO: implement SXHASH and WITH-HASH-TABLE-ITERATOR 36 * May need a rewrite for better performance, and will 37 * need a rewrite if images/bytecode saved on disk. 38 */ 39 40#define GET_HASH 1 41#define PUT_HASH 2 42#define REM_HASH 3 43 44/* 45 * Prototypes 46 */ 47static unsigned long LispHashKey(LispObj*, int); 48static LispObj *LispHash(LispBuiltin*, int); 49static void LispRehash(LispHashTable*); 50static void LispFreeHashEntries(LispHashEntry*, long); 51 52/* 53 * Initialization 54 */ 55extern LispObj *Oeq, *Oeql, *Oequal, *Oequalp; 56 57/* Hash tables will have one of these sizes, unless the user 58 * specified a very large size */ 59static long some_primes[] = { 60 5, 11, 17, 23, 61 31, 47, 71, 97, 62 139, 199, 307, 401, 63 607, 809, 1213, 1619, 64 2437, 3251, 4889, 6521 65}; 66 67/* 68 * Implementation 69 */ 70static unsigned long 71LispHashKey(LispObj *object, int function) 72{ 73 mpi *bigi; 74 char *string; 75 long i, length; 76 unsigned long key = ((unsigned long)object) >> 4; 77 78 /* Must be the same object for EQ */ 79 if (function == FEQ) 80 goto hash_key_done; 81 82 if (function == FEQUALP) { 83 switch (OBJECT_TYPE(object)) { 84 case LispSChar_t: 85 key = (unsigned long)toupper(SCHAR_VALUE(object)); 86 goto hash_key_done; 87 case LispString_t: 88 string = THESTR(object); 89 length = STRLEN(object); 90 if (length > 32) 91 length = 32; 92 for (i = 0, key = 0; i < length; i++) 93 key = (key << 1) ^ toupper(string[i]); 94 goto hash_key_done; 95 default: 96 break; 97 } 98 } 99 100 /* Function is EQL, EQUAL or EQUALP */ 101 switch (OBJECT_TYPE(object)) { 102 case LispFixnum_t: 103 case LispSChar_t: 104 key = (unsigned long)FIXNUM_VALUE(object); 105 goto hash_key_done; 106 case LispInteger_t: 107 key = (unsigned long)INT_VALUE(object); 108 goto hash_key_done; 109 case LispRatio_t: 110 key = (object->data.ratio.numerator << 16) ^ 111 object->data.ratio.denominator; 112 goto hash_key_done; 113 case LispDFloat_t: 114 key = (unsigned long)DFLOAT_VALUE(object); 115 break; 116 case LispComplex_t: 117 key = (LispHashKey(object->data.complex.imag, function) << 16) ^ 118 LispHashKey(object->data.complex.real, function); 119 goto hash_key_done; 120 case LispBignum_t: 121 bigi = object->data.mp.integer; 122 length = bigi->size; 123 if (length > 8) 124 length = 8; 125 key = bigi->sign; 126 for (i = 0; i < length; i++) 127 key = (key << 8) ^ bigi->digs[i]; 128 goto hash_key_done; 129 case LispBigratio_t: 130 bigi = mpr_num(object->data.mp.ratio); 131 length = bigi->size; 132 if (length > 4) 133 length = 4; 134 key = bigi->sign; 135 for (i = 0; i < length; i++) 136 key = (key << 4) ^ bigi->digs[i]; 137 bigi = mpr_den(object->data.mp.ratio); 138 length = bigi->size; 139 if (length > 4) 140 length = 4; 141 for (i = 0; i < length; i++) 142 key = (key << 4) ^ bigi->digs[i]; 143 goto hash_key_done; 144 default: 145 break; 146 } 147 148 /* Anything else must be the same object for EQL */ 149 if (function == FEQL) 150 goto hash_key_done; 151 152 switch (OBJECT_TYPE(object)) { 153 case LispString_t: 154 string = THESTR(object); 155 length = STRLEN(object); 156 if (length > 32) 157 length = 32; 158 for (i = 0, key = 0; i < length; i++) 159 key = (key << 1) ^ string[i]; 160 break; 161 case LispCons_t: 162 key = (LispHashKey(CAR(object), function) << 16) ^ 163 LispHashKey(CDR(object), function); 164 break; 165 case LispQuote_t: 166 case LispBackquote_t: 167 case LispPathname_t: 168 key = LispHashKey(object->data.pathname, function); 169 break; 170 case LispRegex_t: 171 key = LispHashKey(object->data.regex.pattern, function); 172 break; 173 default: 174 break; 175 } 176 177hash_key_done: 178 return (key); 179} 180 181static LispObj * 182LispHash(LispBuiltin *builtin, int code) 183{ 184 LispHashEntry *entry; 185 LispHashTable *hash; 186 unsigned long key; 187 LispObj *result; 188 int found; 189 long i; 190 191 LispObj *okey, *hash_table, *value; 192 193 if (code == REM_HASH) 194 value = NIL; 195 else { 196 value = ARGUMENT(2); 197 if (value == UNSPEC) 198 value = NIL; 199 } 200 hash_table = ARGUMENT(1); 201 okey = ARGUMENT(0); 202 203 CHECK_HASHTABLE(hash_table); 204 205 /* get hash entry */ 206 hash = hash_table->data.hash.table; 207 key = LispHashKey(okey, hash->function) % hash->num_entries; 208 entry = hash->entries + key; 209 210 /* search entry in the hash table */ 211 if (entry->count == 0) 212 i = 0; 213 else { 214 if (hash->function == FEQ) { 215 for (i = entry->cache; i >= 0; i--) { 216 if (entry->keys[i] == okey) 217 goto found_key; 218 } 219 for (i = entry->cache + 1; i < entry->count; i++) { 220 if (entry->keys[i] == okey) 221 break; 222 } 223 } 224 else { 225 for (i = entry->cache; i >= 0; i--) { 226 if (LispObjectCompare(entry->keys[i], okey, 227 hash->function) == T) 228 goto found_key; 229 } 230 for (i = entry->cache + 1; i < entry->count; i++) { 231 if (LispObjectCompare(entry->keys[i], okey, 232 hash->function) == T) 233 break; 234 } 235 } 236 } 237 238found_key: 239 result = value; 240 if ((found = i < entry->count) == 0) 241 i = entry->count; 242 243 switch (code) { 244 case GET_HASH: 245 RETURN_COUNT = 1; 246 if (found) { 247 RETURN(0) = T; 248 entry->cache = i; 249 result = entry->values[i]; 250 } 251 else 252 RETURN(0) = NIL; 253 break; 254 case PUT_HASH: 255 entry->cache = i; 256 if (found) 257 /* Just replace current entry */ 258 entry->values[i] = value; 259 else { 260 if ((i % 4) == 0) { 261 LispObj **keys, **values; 262 263 keys = realloc(entry->keys, sizeof(LispObj*) * (i + 4)); 264 if (keys == NULL) 265 LispDestroy("out of memory"); 266 values = realloc(entry->values, sizeof(LispObj*) * (i + 4)); 267 if (values == NULL) { 268 free(keys); 269 LispDestroy("out of memory"); 270 } 271 entry->keys = keys; 272 entry->values = values; 273 } 274 entry->keys[i] = okey; 275 entry->values[i] = value; 276 ++entry->count; 277 ++hash->count; 278 if (hash->count > hash->rehash_threshold * hash->num_entries) 279 LispRehash(hash); 280 } 281 break; 282 case REM_HASH: 283 if (found) { 284 result = T; 285 --entry->count; 286 --hash->count; 287 if (i < entry->count) { 288 memmove(entry->keys + i, entry->keys + i + 1, 289 (entry->count - i) * sizeof(LispObj*)); 290 memmove(entry->values + i, entry->values + i + 1, 291 (entry->count - i) * sizeof(LispObj*)); 292 } 293 if (entry->cache && entry->cache == entry->count) 294 --entry->cache; 295 } 296 break; 297 } 298 299 return (result); 300} 301 302static void 303LispRehash(LispHashTable *hash) 304{ 305 unsigned long key; 306 LispHashEntry *entries, *nentry, *entry, *last; 307 long i, size = hash->num_entries * hash->rehash_size; 308 309 for (i = 0; i < sizeof(some_primes) / sizeof(some_primes[0]); i++) 310 if (some_primes[i] >= size) { 311 size = some_primes[i]; 312 break; 313 } 314 315 entries = calloc(1, sizeof(LispHashEntry) * size); 316 if (entries == NULL) 317 goto out_of_memory; 318 319 for (entry = hash->entries, last = entry + hash->num_entries; 320 entry < last; entry++) { 321 for (i = 0; i < entry->count; i++) { 322 key = LispHashKey(entry->keys[i], hash->function) % size; 323 nentry = entries + key; 324 if ((nentry->count % 4) == 0) { 325 LispObj **keys, **values; 326 327 keys = realloc(nentry->keys, sizeof(LispObj*) * 328 (nentry->count + 4)); 329 if (keys == NULL) 330 goto out_of_memory; 331 values = realloc(nentry->values, sizeof(LispObj*) * 332 (nentry->count + 4)); 333 if (values == NULL) { 334 free(keys); 335 goto out_of_memory; 336 } 337 nentry->keys = keys; 338 nentry->values = values; 339 } 340 nentry->keys[nentry->count] = entry->keys[i]; 341 nentry->values[nentry->count] = entry->values[i]; 342 ++nentry->count; 343 344 } 345 } 346 LispFreeHashEntries(hash->entries, hash->num_entries); 347 hash->entries = entries; 348 hash->num_entries = size; 349 return; 350 351out_of_memory: 352 if (entries) 353 LispFreeHashEntries(entries, size); 354 LispDestroy("out of memory"); 355} 356 357static void 358LispFreeHashEntries(LispHashEntry *entries, long num_entries) 359{ 360 LispHashEntry *entry, *last; 361 362 for (entry = entries, last = entry + num_entries; entry < last; entry++) { 363 free(entry->keys); 364 free(entry->values); 365 } 366 free(entries); 367} 368 369void 370LispFreeHashTable(LispHashTable *hash) 371{ 372 LispFreeHashEntries(hash->entries, hash->num_entries); 373 free(hash); 374} 375 376LispObj * 377Lisp_Clrhash(LispBuiltin *builtin) 378/* 379 clrhash hash-table 380 */ 381{ 382 LispHashTable *hash; 383 LispHashEntry *entry, *last; 384 385 LispObj *hash_table = ARGUMENT(0); 386 387 CHECK_HASHTABLE(hash_table); 388 389 hash = hash_table->data.hash.table; 390 for (entry = hash->entries, last = entry + hash->num_entries; 391 entry < last; entry++) { 392 free(entry->keys); 393 free(entry->values); 394 entry->keys = entry->values = NULL; 395 entry->count = entry->cache = 0; 396 } 397 hash->count = 0; 398 399 return (hash_table); 400} 401 402LispObj * 403Lisp_Gethash(LispBuiltin *builtin) 404/* 405 gethash key hash-table &optional default 406 */ 407{ 408 return (LispHash(builtin, GET_HASH)); 409} 410 411LispObj * 412Lisp_HashTableP(LispBuiltin *builtin) 413/* 414 hash-table-p object 415 */ 416{ 417 LispObj *object = ARGUMENT(0); 418 419 return (HASHTABLEP(object) ? T : NIL); 420} 421 422LispObj * 423Lisp_HashTableCount(LispBuiltin *builtin) 424/* 425 hash-table-count hash-table 426 */ 427{ 428 LispObj *hash_table = ARGUMENT(0); 429 430 CHECK_HASHTABLE(hash_table); 431 432 return (FIXNUM(hash_table->data.hash.table->count)); 433} 434 435LispObj * 436Lisp_HashTableRehashSize(LispBuiltin *builtin) 437/* 438 hash-table-rehash-size hash-table 439 */ 440{ 441 LispObj *hash_table = ARGUMENT(0); 442 443 CHECK_HASHTABLE(hash_table); 444 445 return (DFLOAT(hash_table->data.hash.table->rehash_size)); 446} 447 448LispObj * 449Lisp_HashTableRehashThreshold(LispBuiltin *builtin) 450/* 451 hash-table-rehash-threshold hash-table 452 */ 453{ 454 LispObj *hash_table = ARGUMENT(0); 455 456 CHECK_HASHTABLE(hash_table); 457 458 return (DFLOAT(hash_table->data.hash.table->rehash_threshold)); 459} 460 461LispObj * 462Lisp_HashTableSize(LispBuiltin *builtin) 463/* 464 hash-table-size hash-table 465 */ 466{ 467 LispObj *hash_table = ARGUMENT(0); 468 469 CHECK_HASHTABLE(hash_table); 470 471 return (FIXNUM(hash_table->data.hash.table->num_entries)); 472} 473 474LispObj * 475Lisp_HashTableTest(LispBuiltin *builtin) 476/* 477 hash-table-test hash-table 478 */ 479{ 480 LispObj *hash_table = ARGUMENT(0); 481 482 CHECK_HASHTABLE(hash_table); 483 484 return (hash_table->data.hash.test); 485} 486 487LispObj * 488Lisp_Maphash(LispBuiltin *builtin) 489/* 490 maphash function hash-table 491 */ 492{ 493 long i; 494 LispHashEntry *entry, *last; 495 496 LispObj *function, *hash_table; 497 498 hash_table = ARGUMENT(1); 499 function = ARGUMENT(0); 500 501 CHECK_HASHTABLE(hash_table); 502 503 for (entry = hash_table->data.hash.table->entries, 504 last = entry + hash_table->data.hash.table->num_entries; 505 entry < last; entry++) { 506 for (i = 0; i < entry->count; i++) 507 APPLY2(function, entry->keys[i], entry->values[i]); 508 } 509 510 return (NIL); 511} 512 513LispObj * 514Lisp_MakeHashTable(LispBuiltin *builtin) 515/* 516 make-hash-table &key test size rehash-size rehash-threshold initial-contents 517 */ 518{ 519 int function = FEQL; 520 unsigned long i, isize, xsize; 521 double drsize, drthreshold; 522 LispHashTable *hash_table; 523 LispObj *cons, *result; 524 525 LispObj *test, *size, *rehash_size, *rehash_threshold, *initial_contents; 526 527 initial_contents = ARGUMENT(4); 528 rehash_threshold = ARGUMENT(3); 529 rehash_size = ARGUMENT(2); 530 size = ARGUMENT(1); 531 test = ARGUMENT(0); 532 533 if (test != UNSPEC) { 534 if (FUNCTIONP(test)) 535 test = test->data.atom->object; 536 if (test == Oeq) 537 function = FEQ; 538 else if (test == Oeql) 539 function = FEQL; 540 else if (test == Oequal) 541 function = FEQUAL; 542 else if (test == Oequalp) 543 function = FEQUALP; 544 else 545 LispDestroy("%s: :TEST must be EQ, EQL, EQUAL, " 546 "or EQUALP, not %s", STRFUN(builtin), STROBJ(test)); 547 } 548 else 549 test = Oeql; 550 551 if (size != UNSPEC) { 552 CHECK_INDEX(size); 553 isize = FIXNUM_VALUE(size); 554 } 555 else 556 isize = 1; 557 558 if (rehash_size != UNSPEC) { 559 CHECK_DFLOAT(rehash_size); 560 if (DFLOAT_VALUE(rehash_size) <= 1.0) 561 LispDestroy("%s: :REHASH-SIZE must a float > 1, not %s", 562 STRFUN(builtin), STROBJ(rehash_size)); 563 drsize = DFLOAT_VALUE(rehash_size); 564 } 565 else 566 drsize = 1.5; 567 568 if (rehash_threshold != UNSPEC) { 569 CHECK_DFLOAT(rehash_threshold); 570 if (DFLOAT_VALUE(rehash_threshold) < 0.0 || 571 DFLOAT_VALUE(rehash_threshold) > 1.0) 572 LispDestroy("%s: :REHASH-THRESHOLD must a float " 573 "in the range 0.0 - 1.0, not %s", 574 STRFUN(builtin), STROBJ(rehash_threshold)); 575 drthreshold = DFLOAT_VALUE(rehash_threshold); 576 } 577 else 578 drthreshold = 0.75; 579 580 if (initial_contents == UNSPEC) 581 initial_contents = NIL; 582 CHECK_LIST(initial_contents); 583 for (xsize = 0, cons = initial_contents; 584 CONSP(cons); 585 xsize++, cons = CDR(cons)) 586 CHECK_CONS(CAR(cons)); 587 588 if (xsize > isize) 589 isize = xsize; 590 591 for (i = 0; i < sizeof(some_primes) / sizeof(some_primes[0]); i++) 592 if (some_primes[i] >= isize) { 593 isize = some_primes[i]; 594 break; 595 } 596 597 hash_table = LispMalloc(sizeof(LispHashTable)); 598 hash_table->entries = LispCalloc(1, sizeof(LispHashEntry) * isize); 599 hash_table->num_entries = isize; 600 hash_table->count = 0; 601 hash_table->function = function; 602 hash_table->rehash_size = drsize; 603 hash_table->rehash_threshold = drthreshold; 604 605 result = LispNew(NIL, NIL); 606 result->type = LispHashTable_t; 607 result->data.hash.table = hash_table; 608 result->data.hash.test = test; 609 610 LispMused(hash_table); 611 LispMused(hash_table->entries); 612 613 if (initial_contents != UNSPEC) { 614 unsigned long key; 615 LispHashEntry *entry; 616 617 for (cons = initial_contents; CONSP(cons); cons = CDR(cons)) { 618 key = LispHashKey(CAAR(cons), function) % isize; 619 entry = hash_table->entries + key; 620 621 if ((entry->count % 4) == 0) { 622 LispObj **keys, **values; 623 624 keys = realloc(entry->keys, sizeof(LispObj*) * (i + 4)); 625 if (keys == NULL) 626 LispDestroy("out of memory"); 627 values = realloc(entry->values, sizeof(LispObj*) * (i + 4)); 628 if (values == NULL) { 629 free(keys); 630 LispDestroy("out of memory"); 631 } 632 entry->keys = keys; 633 entry->values = values; 634 } 635 entry->keys[entry->count] = CAAR(cons); 636 entry->values[entry->count] = CDAR(cons); 637 ++entry->count; 638 } 639 hash_table->count = xsize; 640 } 641 642 return (result); 643} 644 645LispObj * 646Lisp_Remhash(LispBuiltin *builtin) 647/* 648 remhash key hash-table 649 */ 650{ 651 return (LispHash(builtin, REM_HASH)); 652} 653 654LispObj * 655Lisp_XeditPuthash(LispBuiltin *builtin) 656/* 657 lisp::puthash key hash-table value 658 */ 659{ 660 return (LispHash(builtin, PUT_HASH)); 661} 662