1 /******************************************************************* 2 ** d i c t . c 3 ** Forth Inspired Command Language - dictionary methods 4 ** Author: John Sadler (john_sadler@alum.mit.edu) 5 ** Created: 19 July 1997 6 ** $Id: dict.c,v 1.14 2001/12/05 07:21:34 jsadler Exp $ 7 *******************************************************************/ 8 /* 9 ** This file implements the dictionary -- FICL's model of 10 ** memory management. All FICL words are stored in the 11 ** dictionary. A word is a named chunk of data with its 12 ** associated code. FICL treats all words the same, even 13 ** precompiled ones, so your words become first-class 14 ** extensions of the language. You can even define new 15 ** control structures. 16 ** 17 ** 29 jun 1998 (sadler) added variable sized hash table support 18 */ 19 /* 20 ** Copyright (c) 1997-2001 John Sadler (john_sadler@alum.mit.edu) 21 ** All rights reserved. 22 ** 23 ** Get the latest Ficl release at http://ficl.sourceforge.net 24 ** 25 ** I am interested in hearing from anyone who uses ficl. If you have 26 ** a problem, a success story, a defect, an enhancement request, or 27 ** if you would like to contribute to the ficl release, please 28 ** contact me by email at the address above. 29 ** 30 ** L I C E N S E and D I S C L A I M E R 31 ** 32 ** Redistribution and use in source and binary forms, with or without 33 ** modification, are permitted provided that the following conditions 34 ** are met: 35 ** 1. Redistributions of source code must retain the above copyright 36 ** notice, this list of conditions and the following disclaimer. 37 ** 2. Redistributions in binary form must reproduce the above copyright 38 ** notice, this list of conditions and the following disclaimer in the 39 ** documentation and/or other materials provided with the distribution. 40 ** 41 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 42 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 43 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 44 ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 45 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 46 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 47 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 48 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 49 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 50 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 51 ** SUCH DAMAGE. 52 */ 53 54 /* $FreeBSD$ */ 55 56 #ifdef TESTMAIN 57 #include <stdio.h> 58 #include <ctype.h> 59 #else 60 #include <stand.h> 61 #endif 62 #include <string.h> 63 #include "ficl.h" 64 65 /* Dictionary on-demand resizing control variables */ 66 CELL dictThreshold; 67 CELL dictIncrease; 68 69 70 static char *dictCopyName(FICL_DICT *pDict, STRINGINFO si); 71 72 /************************************************************************** 73 d i c t A b o r t D e f i n i t i o n 74 ** Abort a definition in process: reclaim its memory and unlink it 75 ** from the dictionary list. Assumes that there is a smudged 76 ** definition in process...otherwise does nothing. 77 ** NOTE: this function is not smart enough to unlink a word that 78 ** has been successfully defined (ie linked into a hash). It 79 ** only works for defs in process. If the def has been unsmudged, 80 ** nothing happens. 81 **************************************************************************/ 82 void dictAbortDefinition(FICL_DICT *pDict) 83 { 84 FICL_WORD *pFW; 85 ficlLockDictionary(TRUE); 86 pFW = pDict->smudge; 87 88 if (pFW->flags & FW_SMUDGE) 89 pDict->here = (CELL *)pFW->name; 90 91 ficlLockDictionary(FALSE); 92 return; 93 } 94 95 96 /************************************************************************** 97 a l i g n P t r 98 ** Aligns the given pointer to FICL_ALIGN address units. 99 ** Returns the aligned pointer value. 100 **************************************************************************/ 101 void *alignPtr(void *ptr) 102 { 103 #if FICL_ALIGN > 0 104 char *cp; 105 CELL c; 106 cp = (char *)ptr + FICL_ALIGN_ADD; 107 c.p = (void *)cp; 108 c.u = c.u & (~FICL_ALIGN_ADD); 109 ptr = (CELL *)c.p; 110 #endif 111 return ptr; 112 } 113 114 115 /************************************************************************** 116 d i c t A l i g n 117 ** Align the dictionary's free space pointer 118 **************************************************************************/ 119 void dictAlign(FICL_DICT *pDict) 120 { 121 pDict->here = alignPtr(pDict->here); 122 } 123 124 125 /************************************************************************** 126 d i c t A l l o t 127 ** Allocate or remove n chars of dictionary space, with 128 ** checks for underrun and overrun 129 **************************************************************************/ 130 int dictAllot(FICL_DICT *pDict, int n) 131 { 132 char *cp = (char *)pDict->here; 133 #if FICL_ROBUST 134 if (n > 0) 135 { 136 if ((unsigned)n <= dictCellsAvail(pDict) * sizeof (CELL)) 137 cp += n; 138 else 139 return 1; /* dict is full */ 140 } 141 else 142 { 143 n = -n; 144 if ((unsigned)n <= dictCellsUsed(pDict) * sizeof (CELL)) 145 cp -= n; 146 else /* prevent underflow */ 147 cp -= dictCellsUsed(pDict) * sizeof (CELL); 148 } 149 #else 150 cp += n; 151 #endif 152 pDict->here = PTRtoCELL cp; 153 return 0; 154 } 155 156 157 /************************************************************************** 158 d i c t A l l o t C e l l s 159 ** Reserve space for the requested number of cells in the 160 ** dictionary. If nCells < 0 , removes space from the dictionary. 161 **************************************************************************/ 162 int dictAllotCells(FICL_DICT *pDict, int nCells) 163 { 164 #if FICL_ROBUST 165 if (nCells > 0) 166 { 167 if (nCells <= dictCellsAvail(pDict)) 168 pDict->here += nCells; 169 else 170 return 1; /* dict is full */ 171 } 172 else 173 { 174 nCells = -nCells; 175 if (nCells <= dictCellsUsed(pDict)) 176 pDict->here -= nCells; 177 else /* prevent underflow */ 178 pDict->here -= dictCellsUsed(pDict); 179 } 180 #else 181 pDict->here += nCells; 182 #endif 183 return 0; 184 } 185 186 187 /************************************************************************** 188 d i c t A p p e n d C e l l 189 ** Append the specified cell to the dictionary 190 **************************************************************************/ 191 void dictAppendCell(FICL_DICT *pDict, CELL c) 192 { 193 *pDict->here++ = c; 194 return; 195 } 196 197 198 /************************************************************************** 199 d i c t A p p e n d C h a r 200 ** Append the specified char to the dictionary 201 **************************************************************************/ 202 void dictAppendChar(FICL_DICT *pDict, char c) 203 { 204 char *cp = (char *)pDict->here; 205 *cp++ = c; 206 pDict->here = PTRtoCELL cp; 207 return; 208 } 209 210 211 /************************************************************************** 212 d i c t A p p e n d W o r d 213 ** Create a new word in the dictionary with the specified 214 ** name, code, and flags. Name must be NULL-terminated. 215 **************************************************************************/ 216 FICL_WORD *dictAppendWord(FICL_DICT *pDict, 217 char *name, 218 FICL_CODE pCode, 219 UNS8 flags) 220 { 221 STRINGINFO si; 222 SI_SETLEN(si, strlen(name)); 223 SI_SETPTR(si, name); 224 return dictAppendWord2(pDict, si, pCode, flags); 225 } 226 227 228 /************************************************************************** 229 d i c t A p p e n d W o r d 2 230 ** Create a new word in the dictionary with the specified 231 ** STRINGINFO, code, and flags. Does not require a NULL-terminated 232 ** name. 233 **************************************************************************/ 234 FICL_WORD *dictAppendWord2(FICL_DICT *pDict, 235 STRINGINFO si, 236 FICL_CODE pCode, 237 UNS8 flags) 238 { 239 FICL_COUNT len = (FICL_COUNT)SI_COUNT(si); 240 char *pName; 241 FICL_WORD *pFW; 242 243 ficlLockDictionary(TRUE); 244 245 /* 246 ** NOTE: dictCopyName advances "here" as a side-effect. 247 ** It must execute before pFW is initialized. 248 */ 249 pName = dictCopyName(pDict, si); 250 pFW = (FICL_WORD *)pDict->here; 251 pDict->smudge = pFW; 252 pFW->hash = hashHashCode(si); 253 pFW->code = pCode; 254 pFW->flags = (UNS8)(flags | FW_SMUDGE); 255 pFW->nName = (char)len; 256 pFW->name = pName; 257 /* 258 ** Point "here" to first cell of new word's param area... 259 */ 260 pDict->here = pFW->param; 261 262 if (!(flags & FW_SMUDGE)) 263 dictUnsmudge(pDict); 264 265 ficlLockDictionary(FALSE); 266 return pFW; 267 } 268 269 270 /************************************************************************** 271 d i c t A p p e n d U N S 272 ** Append the specified FICL_UNS to the dictionary 273 **************************************************************************/ 274 void dictAppendUNS(FICL_DICT *pDict, FICL_UNS u) 275 { 276 *pDict->here++ = LVALUEtoCELL(u); 277 return; 278 } 279 280 281 /************************************************************************** 282 d i c t C e l l s A v a i l 283 ** Returns the number of empty cells left in the dictionary 284 **************************************************************************/ 285 int dictCellsAvail(FICL_DICT *pDict) 286 { 287 return pDict->size - dictCellsUsed(pDict); 288 } 289 290 291 /************************************************************************** 292 d i c t C e l l s U s e d 293 ** Returns the number of cells consumed in the dicionary 294 **************************************************************************/ 295 int dictCellsUsed(FICL_DICT *pDict) 296 { 297 return pDict->here - pDict->dict; 298 } 299 300 301 /************************************************************************** 302 d i c t C h e c k 303 ** Checks the dictionary for corruption and throws appropriate 304 ** errors. 305 ** Input: +n number of ADDRESS UNITS (not Cells) proposed to allot 306 ** -n number of ADDRESS UNITS proposed to de-allot 307 ** 0 just do a consistency check 308 **************************************************************************/ 309 void dictCheck(FICL_DICT *pDict, FICL_VM *pVM, int n) 310 { 311 if ((n >= 0) && (dictCellsAvail(pDict) * (int)sizeof(CELL) < n)) 312 { 313 vmThrowErr(pVM, "Error: dictionary full"); 314 } 315 316 if ((n <= 0) && (dictCellsUsed(pDict) * (int)sizeof(CELL) < -n)) 317 { 318 vmThrowErr(pVM, "Error: dictionary underflow"); 319 } 320 321 if (pDict->nLists > FICL_DEFAULT_VOCS) 322 { 323 dictResetSearchOrder(pDict); 324 vmThrowErr(pVM, "Error: search order overflow"); 325 } 326 else if (pDict->nLists < 0) 327 { 328 dictResetSearchOrder(pDict); 329 vmThrowErr(pVM, "Error: search order underflow"); 330 } 331 332 return; 333 } 334 335 336 /************************************************************************** 337 d i c t C o p y N a m e 338 ** Copy up to nFICLNAME characters of the name specified by si into 339 ** the dictionary starting at "here", then NULL-terminate the name, 340 ** point "here" to the next available byte, and return the address of 341 ** the beginning of the name. Used by dictAppendWord. 342 ** N O T E S : 343 ** 1. "here" is guaranteed to be aligned after this operation. 344 ** 2. If the string has zero length, align and return "here" 345 **************************************************************************/ 346 static char *dictCopyName(FICL_DICT *pDict, STRINGINFO si) 347 { 348 char *oldCP = (char *)pDict->here; 349 char *cp = oldCP; 350 char *name = SI_PTR(si); 351 int i = SI_COUNT(si); 352 353 if (i == 0) 354 { 355 dictAlign(pDict); 356 return (char *)pDict->here; 357 } 358 359 if (i > nFICLNAME) 360 i = nFICLNAME; 361 362 for (; i > 0; --i) 363 { 364 *cp++ = *name++; 365 } 366 367 *cp++ = '\0'; 368 369 pDict->here = PTRtoCELL cp; 370 dictAlign(pDict); 371 return oldCP; 372 } 373 374 375 /************************************************************************** 376 d i c t C r e a t e 377 ** Create and initialize a dictionary with the specified number 378 ** of cells capacity, and no hashing (hash size == 1). 379 **************************************************************************/ 380 FICL_DICT *dictCreate(unsigned nCells) 381 { 382 return dictCreateHashed(nCells, 1); 383 } 384 385 386 FICL_DICT *dictCreateHashed(unsigned nCells, unsigned nHash) 387 { 388 FICL_DICT *pDict; 389 size_t nAlloc; 390 391 nAlloc = sizeof (FICL_HASH) + nCells * sizeof (CELL) 392 + (nHash - 1) * sizeof (FICL_WORD *); 393 394 pDict = ficlMalloc(sizeof (FICL_DICT)); 395 assert(pDict); 396 memset(pDict, 0, sizeof (FICL_DICT)); 397 pDict->dict = ficlMalloc(nAlloc); 398 assert(pDict->dict); 399 400 pDict->size = nCells; 401 dictEmpty(pDict, nHash); 402 return pDict; 403 } 404 405 406 /************************************************************************** 407 d i c t C r e a t e W o r d l i s t 408 ** Create and initialize an anonymous wordlist 409 **************************************************************************/ 410 FICL_HASH *dictCreateWordlist(FICL_DICT *dp, int nBuckets) 411 { 412 FICL_HASH *pHash; 413 414 dictAlign(dp); 415 pHash = (FICL_HASH *)dp->here; 416 dictAllot(dp, sizeof (FICL_HASH) 417 + (nBuckets-1) * sizeof (FICL_WORD *)); 418 419 pHash->size = nBuckets; 420 hashReset(pHash); 421 return pHash; 422 } 423 424 425 /************************************************************************** 426 d i c t D e l e t e 427 ** Free all memory allocated for the given dictionary 428 **************************************************************************/ 429 void dictDelete(FICL_DICT *pDict) 430 { 431 assert(pDict); 432 ficlFree(pDict); 433 return; 434 } 435 436 437 /************************************************************************** 438 d i c t E m p t y 439 ** Empty the dictionary, reset its hash table, and reset its search order. 440 ** Clears and (re-)creates the hash table with the size specified by nHash. 441 **************************************************************************/ 442 void dictEmpty(FICL_DICT *pDict, unsigned nHash) 443 { 444 FICL_HASH *pHash; 445 446 pDict->here = pDict->dict; 447 448 dictAlign(pDict); 449 pHash = (FICL_HASH *)pDict->here; 450 dictAllot(pDict, 451 sizeof (FICL_HASH) + (nHash - 1) * sizeof (FICL_WORD *)); 452 453 pHash->size = nHash; 454 hashReset(pHash); 455 456 pDict->pForthWords = pHash; 457 pDict->smudge = NULL; 458 dictResetSearchOrder(pDict); 459 return; 460 } 461 462 463 /************************************************************************** 464 d i c t H a s h S u m m a r y 465 ** Calculate a figure of merit for the dictionary hash table based 466 ** on the average search depth for all the words in the dictionary, 467 ** assuming uniform distribution of target keys. The figure of merit 468 ** is the ratio of the total search depth for all keys in the table 469 ** versus a theoretical optimum that would be achieved if the keys 470 ** were distributed into the table as evenly as possible. 471 ** The figure would be worse if the hash table used an open 472 ** addressing scheme (i.e. collisions resolved by searching the 473 ** table for an empty slot) for a given size table. 474 **************************************************************************/ 475 #if FICL_WANT_FLOAT 476 void dictHashSummary(FICL_VM *pVM) 477 { 478 FICL_DICT *dp = vmGetDict(pVM); 479 FICL_HASH *pFHash; 480 FICL_WORD **pHash; 481 unsigned size; 482 FICL_WORD *pFW; 483 unsigned i; 484 int nMax = 0; 485 int nWords = 0; 486 int nFilled; 487 double avg = 0.0; 488 double best; 489 int nAvg, nRem, nDepth; 490 491 dictCheck(dp, pVM, 0); 492 493 pFHash = dp->pSearch[dp->nLists - 1]; 494 pHash = pFHash->table; 495 size = pFHash->size; 496 nFilled = size; 497 498 for (i = 0; i < size; i++) 499 { 500 int n = 0; 501 pFW = pHash[i]; 502 503 while (pFW) 504 { 505 ++n; 506 ++nWords; 507 pFW = pFW->link; 508 } 509 510 avg += (double)(n * (n+1)) / 2.0; 511 512 if (n > nMax) 513 nMax = n; 514 if (n == 0) 515 --nFilled; 516 } 517 518 /* Calc actual avg search depth for this hash */ 519 avg = avg / nWords; 520 521 /* Calc best possible performance with this size hash */ 522 nAvg = nWords / size; 523 nRem = nWords % size; 524 nDepth = size * (nAvg * (nAvg+1))/2 + (nAvg+1)*nRem; 525 best = (double)nDepth/nWords; 526 527 sprintf(pVM->pad, 528 "%d bins, %2.0f%% filled, Depth: Max=%d, Avg=%2.1f, Best=%2.1f, Score: %2.0f%%", 529 size, 530 (double)nFilled * 100.0 / size, nMax, 531 avg, 532 best, 533 100.0 * best / avg); 534 535 ficlTextOut(pVM, pVM->pad, 1); 536 537 return; 538 } 539 #endif 540 541 /************************************************************************** 542 d i c t I n c l u d e s 543 ** Returns TRUE iff the given pointer is within the address range of 544 ** the dictionary. 545 **************************************************************************/ 546 int dictIncludes(FICL_DICT *pDict, void *p) 547 { 548 return ((p >= (void *) &pDict->dict) 549 && (p < (void *)(&pDict->dict + pDict->size)) 550 ); 551 } 552 553 /************************************************************************** 554 d i c t L o o k u p 555 ** Find the FICL_WORD that matches the given name and length. 556 ** If found, returns the word's address. Otherwise returns NULL. 557 ** Uses the search order list to search multiple wordlists. 558 **************************************************************************/ 559 FICL_WORD *dictLookup(FICL_DICT *pDict, STRINGINFO si) 560 { 561 FICL_WORD *pFW = NULL; 562 FICL_HASH *pHash; 563 int i; 564 UNS16 hashCode = hashHashCode(si); 565 566 assert(pDict); 567 568 ficlLockDictionary(1); 569 570 for (i = (int)pDict->nLists - 1; (i >= 0) && (!pFW); --i) 571 { 572 pHash = pDict->pSearch[i]; 573 pFW = hashLookup(pHash, si, hashCode); 574 } 575 576 ficlLockDictionary(0); 577 return pFW; 578 } 579 580 581 /************************************************************************** 582 f i c l L o o k u p L o c 583 ** Same as dictLookup, but looks in system locals dictionary first... 584 ** Assumes locals dictionary has only one wordlist... 585 **************************************************************************/ 586 #if FICL_WANT_LOCALS 587 FICL_WORD *ficlLookupLoc(FICL_SYSTEM *pSys, STRINGINFO si) 588 { 589 FICL_WORD *pFW = NULL; 590 FICL_DICT *pDict = pSys->dp; 591 FICL_HASH *pHash = ficlGetLoc(pSys)->pForthWords; 592 int i; 593 UNS16 hashCode = hashHashCode(si); 594 595 assert(pHash); 596 assert(pDict); 597 598 ficlLockDictionary(1); 599 /* 600 ** check the locals dict first... 601 */ 602 pFW = hashLookup(pHash, si, hashCode); 603 604 /* 605 ** If no joy, (!pFW) --------------------------v 606 ** iterate over the search list in the main dict 607 */ 608 for (i = (int)pDict->nLists - 1; (i >= 0) && (!pFW); --i) 609 { 610 pHash = pDict->pSearch[i]; 611 pFW = hashLookup(pHash, si, hashCode); 612 } 613 614 ficlLockDictionary(0); 615 return pFW; 616 } 617 #endif 618 619 620 /************************************************************************** 621 d i c t R e s e t S e a r c h O r d e r 622 ** Initialize the dictionary search order list to sane state 623 **************************************************************************/ 624 void dictResetSearchOrder(FICL_DICT *pDict) 625 { 626 assert(pDict); 627 pDict->pCompile = pDict->pForthWords; 628 pDict->nLists = 1; 629 pDict->pSearch[0] = pDict->pForthWords; 630 return; 631 } 632 633 634 /************************************************************************** 635 d i c t S e t F l a g s 636 ** Changes the flags field of the most recently defined word: 637 ** Set all bits that are ones in the set parameter, clear all bits 638 ** that are ones in the clr parameter. Clear wins in case the same bit 639 ** is set in both parameters. 640 **************************************************************************/ 641 void dictSetFlags(FICL_DICT *pDict, UNS8 set, UNS8 clr) 642 { 643 assert(pDict->smudge); 644 pDict->smudge->flags |= set; 645 pDict->smudge->flags &= ~clr; 646 return; 647 } 648 649 650 /************************************************************************** 651 d i c t S e t I m m e d i a t e 652 ** Set the most recently defined word as IMMEDIATE 653 **************************************************************************/ 654 void dictSetImmediate(FICL_DICT *pDict) 655 { 656 assert(pDict->smudge); 657 pDict->smudge->flags |= FW_IMMEDIATE; 658 return; 659 } 660 661 662 /************************************************************************** 663 d i c t U n s m u d g e 664 ** Completes the definition of a word by linking it 665 ** into the main list 666 **************************************************************************/ 667 void dictUnsmudge(FICL_DICT *pDict) 668 { 669 FICL_WORD *pFW = pDict->smudge; 670 FICL_HASH *pHash = pDict->pCompile; 671 672 assert(pHash); 673 assert(pFW); 674 /* 675 ** :noname words never get linked into the list... 676 */ 677 if (pFW->nName > 0) 678 hashInsertWord(pHash, pFW); 679 pFW->flags &= ~(FW_SMUDGE); 680 return; 681 } 682 683 684 /************************************************************************** 685 d i c t W h e r e 686 ** Returns the value of the HERE pointer -- the address 687 ** of the next free cell in the dictionary 688 **************************************************************************/ 689 CELL *dictWhere(FICL_DICT *pDict) 690 { 691 return pDict->here; 692 } 693 694 695 /************************************************************************** 696 h a s h F o r g e t 697 ** Unlink all words in the hash that have addresses greater than or 698 ** equal to the address supplied. Implementation factor for FORGET 699 ** and MARKER. 700 **************************************************************************/ 701 void hashForget(FICL_HASH *pHash, void *where) 702 { 703 FICL_WORD *pWord; 704 unsigned i; 705 706 assert(pHash); 707 assert(where); 708 709 for (i = 0; i < pHash->size; i++) 710 { 711 pWord = pHash->table[i]; 712 713 while ((void *)pWord >= where) 714 { 715 pWord = pWord->link; 716 } 717 718 pHash->table[i] = pWord; 719 } 720 721 return; 722 } 723 724 725 /************************************************************************** 726 h a s h H a s h C o d e 727 ** 728 ** Generate a 16 bit hashcode from a character string using a rolling 729 ** shift and add stolen from PJ Weinberger of Bell Labs fame. Case folds 730 ** the name before hashing it... 731 ** N O T E : If string has zero length, returns zero. 732 **************************************************************************/ 733 UNS16 hashHashCode(STRINGINFO si) 734 { 735 /* hashPJW */ 736 UNS8 *cp; 737 UNS16 code = (UNS16)si.count; 738 UNS16 shift = 0; 739 740 if (si.count == 0) 741 return 0; 742 743 /* changed to run without errors under Purify -- lch */ 744 for (cp = (UNS8 *)si.cp; si.count && *cp; cp++, si.count--) 745 { 746 code = (UNS16)((code << 4) + tolower(*cp)); 747 shift = (UNS16)(code & 0xf000); 748 if (shift) 749 { 750 code ^= (UNS16)(shift >> 8); 751 code ^= (UNS16)shift; 752 } 753 } 754 755 return (UNS16)code; 756 } 757 758 759 760 761 /************************************************************************** 762 h a s h I n s e r t W o r d 763 ** Put a word into the hash table using the word's hashcode as 764 ** an index (modulo the table size). 765 **************************************************************************/ 766 void hashInsertWord(FICL_HASH *pHash, FICL_WORD *pFW) 767 { 768 FICL_WORD **pList; 769 770 assert(pHash); 771 assert(pFW); 772 773 if (pHash->size == 1) 774 { 775 pList = pHash->table; 776 } 777 else 778 { 779 pList = pHash->table + (pFW->hash % pHash->size); 780 } 781 782 pFW->link = *pList; 783 *pList = pFW; 784 return; 785 } 786 787 788 /************************************************************************** 789 h a s h L o o k u p 790 ** Find a name in the hash table given the hashcode and text of the name. 791 ** Returns the address of the corresponding FICL_WORD if found, 792 ** otherwise NULL. 793 ** Note: outer loop on link field supports inheritance in wordlists. 794 ** It's not part of ANS Forth - ficl only. hashReset creates wordlists 795 ** with NULL link fields. 796 **************************************************************************/ 797 FICL_WORD *hashLookup(FICL_HASH *pHash, STRINGINFO si, UNS16 hashCode) 798 { 799 FICL_UNS nCmp = si.count; 800 FICL_WORD *pFW; 801 UNS16 hashIdx; 802 803 if (nCmp > nFICLNAME) 804 nCmp = nFICLNAME; 805 806 for (; pHash != NULL; pHash = pHash->link) 807 { 808 if (pHash->size > 1) 809 hashIdx = (UNS16)(hashCode % pHash->size); 810 else /* avoid the modulo op for single threaded lists */ 811 hashIdx = 0; 812 813 for (pFW = pHash->table[hashIdx]; pFW; pFW = pFW->link) 814 { 815 if ( (pFW->nName == si.count) 816 && (!strincmp(si.cp, pFW->name, nCmp)) ) 817 return pFW; 818 #if FICL_ROBUST 819 assert(pFW != pFW->link); 820 #endif 821 } 822 } 823 824 return NULL; 825 } 826 827 828 /************************************************************************** 829 h a s h R e s e t 830 ** Initialize a FICL_HASH to empty state. 831 **************************************************************************/ 832 void hashReset(FICL_HASH *pHash) 833 { 834 unsigned i; 835 836 assert(pHash); 837 838 for (i = 0; i < pHash->size; i++) 839 { 840 pHash->table[i] = NULL; 841 } 842 843 pHash->link = NULL; 844 pHash->name = NULL; 845 return; 846 } 847 848 /************************************************************************** 849 d i c t C h e c k T h r e s h o l d 850 ** Verify if an increase in the dictionary size is warranted, and do it if 851 ** so. 852 **************************************************************************/ 853 854 void dictCheckThreshold(FICL_DICT* dp) 855 { 856 if( dictCellsAvail(dp) < dictThreshold.u ) { 857 dp->dict = ficlMalloc( dictIncrease.u * sizeof (CELL) ); 858 assert(dp->dict); 859 dp->here = dp->dict; 860 dp->size = dictIncrease.u; 861 dictAlign(dp); 862 } 863 } 864 865