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