1 /*******************************************************************
3 ** Forth Inspired Command Language - dictionary methods
4 ** Author: John Sadler (john_sadler@alum.mit.edu)
5 ** Created: 19 July 1997
7 *******************************************************************/
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.
17 ** 29 jun 1998 (sadler) added variable sized hash table support
20 /* $FreeBSD: src/sys/boot/ficl/dict.c,v 1.6.2.1 2000/07/06 23:51:45 obrien Exp $ */
32 /* Dictionary on-demand resizing control variables */
33 unsigned int dictThreshold;
34 unsigned int dictIncrease;
37 static char *dictCopyName(FICL_DICT *pDict, STRINGINFO si);
39 /**************************************************************************
40 d i c t A b o r t D e f i n i t i o n
41 ** Abort a definition in process: reclaim its memory and unlink it
42 ** from the dictionary list. Assumes that there is a smudged
43 ** definition in process...otherwise does nothing.
44 ** NOTE: this function is not smart enough to unlink a word that
45 ** has been successfully defined (ie linked into a hash). It
46 ** only works for defs in process. If the def has been unsmudged,
48 **************************************************************************/
49 void dictAbortDefinition(FICL_DICT *pDict)
52 ficlLockDictionary(TRUE);
55 if (pFW->flags & FW_SMUDGE)
56 pDict->here = (CELL *)pFW->name;
58 ficlLockDictionary(FALSE);
63 /**************************************************************************
65 ** Aligns the given pointer to FICL_ALIGN address units.
66 ** Returns the aligned pointer value.
67 **************************************************************************/
68 void *alignPtr(void *ptr)
73 cp = (char *)ptr + FICL_ALIGN_ADD;
75 c.u = c.u & (~FICL_ALIGN_ADD);
82 /**************************************************************************
84 ** Align the dictionary's free space pointer
85 **************************************************************************/
86 void dictAlign(FICL_DICT *pDict)
88 pDict->here = alignPtr(pDict->here);
92 /**************************************************************************
94 ** Allocate or remove n chars of dictionary space, with
95 ** checks for underrun and overrun
96 **************************************************************************/
97 int dictAllot(FICL_DICT *pDict, int n)
99 char *cp = (char *)pDict->here;
103 if ((unsigned)n <= dictCellsAvail(pDict) * sizeof (CELL))
106 return 1; /* dict is full */
111 if ((unsigned)n <= dictCellsUsed(pDict) * sizeof (CELL))
113 else /* prevent underflow */
114 cp -= dictCellsUsed(pDict) * sizeof (CELL);
119 pDict->here = PTRtoCELL cp;
124 /**************************************************************************
125 d i c t A l l o t C e l l s
126 ** Reserve space for the requested number of cells in the
127 ** dictionary. If nCells < 0 , removes space from the dictionary.
128 **************************************************************************/
129 int dictAllotCells(FICL_DICT *pDict, int nCells)
134 if (nCells <= dictCellsAvail(pDict))
135 pDict->here += nCells;
137 return 1; /* dict is full */
142 if (nCells <= dictCellsUsed(pDict))
143 pDict->here -= nCells;
144 else /* prevent underflow */
145 pDict->here -= dictCellsUsed(pDict);
148 pDict->here += nCells;
154 /**************************************************************************
155 d i c t A p p e n d C e l l
156 ** Append the specified cell to the dictionary
157 **************************************************************************/
158 void dictAppendCell(FICL_DICT *pDict, CELL c)
165 /**************************************************************************
166 d i c t A p p e n d C h a r
167 ** Append the specified char to the dictionary
168 **************************************************************************/
169 void dictAppendChar(FICL_DICT *pDict, char c)
171 char *cp = (char *)pDict->here;
173 pDict->here = PTRtoCELL cp;
178 /**************************************************************************
179 d i c t A p p e n d W o r d
180 ** Create a new word in the dictionary with the specified
181 ** name, code, and flags. Name must be NULL-terminated.
182 **************************************************************************/
183 FICL_WORD *dictAppendWord(FICL_DICT *pDict,
189 SI_SETLEN(si, strlen(name));
191 return dictAppendWord2(pDict, si, pCode, flags);
195 /**************************************************************************
196 d i c t A p p e n d W o r d 2
197 ** Create a new word in the dictionary with the specified
198 ** STRINGINFO, code, and flags. Does not require a NULL-terminated
200 **************************************************************************/
201 FICL_WORD *dictAppendWord2(FICL_DICT *pDict,
206 FICL_COUNT len = (FICL_COUNT)SI_COUNT(si);
210 ficlLockDictionary(TRUE);
213 ** NOTE: dictCopyName advances "here" as a side-effect.
214 ** It must execute before pFW is initialized.
216 pName = dictCopyName(pDict, si);
217 pFW = (FICL_WORD *)pDict->here;
219 pFW->hash = hashHashCode(si);
221 pFW->flags = (UNS8)(flags | FW_SMUDGE);
222 pFW->nName = (char)len;
225 ** Point "here" to first cell of new word's param area...
227 pDict->here = pFW->param;
229 if (!(flags & FW_SMUDGE))
232 ficlLockDictionary(FALSE);
237 /**************************************************************************
238 d i c t A p p e n d U N S
239 ** Append the specified FICL_UNS to the dictionary
240 **************************************************************************/
241 void dictAppendUNS(FICL_DICT *pDict, FICL_UNS u)
243 *pDict->here++ = LVALUEtoCELL(u);
248 /**************************************************************************
249 d i c t C e l l s A v a i l
250 ** Returns the number of empty cells left in the dictionary
251 **************************************************************************/
252 int dictCellsAvail(FICL_DICT *pDict)
254 return pDict->size - dictCellsUsed(pDict);
258 /**************************************************************************
259 d i c t C e l l s U s e d
260 ** Returns the number of cells consumed in the dicionary
261 **************************************************************************/
262 int dictCellsUsed(FICL_DICT *pDict)
264 return pDict->here - pDict->dict;
268 /**************************************************************************
270 ** Checks the dictionary for corruption and throws appropriate
272 **************************************************************************/
273 void dictCheck(FICL_DICT *pDict, FICL_VM *pVM, int n)
275 if ((n >= 0) && (dictCellsAvail(pDict) * sizeof (CELL) < n))
277 vmThrowErr(pVM, "Error: dictionary full");
280 if ((n <= 0) && (dictCellsUsed(pDict) * sizeof (CELL) < -n))
282 vmThrowErr(pVM, "Error: dictionary underflow");
285 if (pDict->nLists > FICL_DEFAULT_VOCS)
287 dictResetSearchOrder(pDict);
288 vmThrowErr(pVM, "Error: search order overflow");
290 else if (pDict->nLists < 0)
292 dictResetSearchOrder(pDict);
293 vmThrowErr(pVM, "Error: search order underflow");
300 /**************************************************************************
301 d i c t C o p y N a m e
302 ** Copy up to nFICLNAME characters of the name specified by si into
303 ** the dictionary starting at "here", then NULL-terminate the name,
304 ** point "here" to the next available byte, and return the address of
305 ** the beginning of the name. Used by dictAppendWord.
307 ** 1. "here" is guaranteed to be aligned after this operation.
308 ** 2. If the string has zero length, align and return "here"
309 **************************************************************************/
310 static char *dictCopyName(FICL_DICT *pDict, STRINGINFO si)
312 char *oldCP = (char *)pDict->here;
314 char *name = SI_PTR(si);
315 int i = SI_COUNT(si);
320 return (char *)pDict->here;
333 pDict->here = PTRtoCELL cp;
339 /**************************************************************************
341 ** Create and initialize a dictionary with the specified number
342 ** of cells capacity, and no hashing (hash size == 1).
343 **************************************************************************/
344 FICL_DICT *dictCreate(unsigned nCells)
346 return dictCreateHashed(nCells, 1);
350 FICL_DICT *dictCreateHashed(unsigned nCells, unsigned nHash)
355 nAlloc = sizeof (FICL_HASH) + nCells * sizeof (CELL)
356 + (nHash - 1) * sizeof (FICL_WORD *);
358 pDict = ficlMalloc(sizeof (FICL_DICT));
360 memset(pDict, 0, sizeof (FICL_DICT));
361 pDict->dict = ficlMalloc(nAlloc);
363 pDict->size = nCells;
364 dictEmpty(pDict, nHash);
369 /**************************************************************************
371 ** Free all memory allocated for the given dictionary
372 **************************************************************************/
373 void dictDelete(FICL_DICT *pDict)
381 /**************************************************************************
383 ** Empty the dictionary, reset its hash table, and reset its search order.
384 ** Clears and (re-)creates the hash table with the size specified by nHash.
385 **************************************************************************/
386 void dictEmpty(FICL_DICT *pDict, unsigned nHash)
390 pDict->here = pDict->dict;
393 pHash = (FICL_HASH *)pDict->here;
395 sizeof (FICL_HASH) + (nHash - 1) * sizeof (FICL_WORD *));
400 pDict->pForthWords = pHash;
401 pDict->smudge = NULL;
402 dictResetSearchOrder(pDict);
407 /**************************************************************************
408 d i c t I n c l u d e s
409 ** Returns TRUE iff the given pointer is within the address range of
411 **************************************************************************/
412 int dictIncludes(FICL_DICT *pDict, void *p)
414 return ((p >= (void *) &pDict->dict)
415 && (p < (void *)(&pDict->dict + pDict->size))
420 /**************************************************************************
422 ** Find the FICL_WORD that matches the given name and length.
423 ** If found, returns the word's address. Otherwise returns NULL.
424 ** Uses the search order list to search multiple wordlists.
425 **************************************************************************/
426 FICL_WORD *dictLookup(FICL_DICT *pDict, STRINGINFO si)
428 FICL_WORD *pFW = NULL;
431 UNS16 hashCode = hashHashCode(si);
435 ficlLockDictionary(1);
437 for (i = (int)pDict->nLists - 1; (i >= 0) && (!pFW); --i)
439 pHash = pDict->pSearch[i];
440 pFW = hashLookup(pHash, si, hashCode);
443 ficlLockDictionary(0);
448 /**************************************************************************
449 d i c t L o o k u p L o c
450 ** Same as dictLookup, but looks in system locals dictionary first...
451 ** Assumes locals dictionary has only one wordlist...
452 **************************************************************************/
454 FICL_WORD *dictLookupLoc(FICL_DICT *pDict, STRINGINFO si)
456 FICL_WORD *pFW = NULL;
457 FICL_HASH *pHash = ficlGetLoc()->pForthWords;
459 UNS16 hashCode = hashHashCode(si);
464 ficlLockDictionary(1);
466 ** check the locals dict first...
468 pFW = hashLookup(pHash, si, hashCode);
471 ** If no joy, (!pFW) --------------------------v
472 ** iterate over the search list in the main dict
474 for (i = (int)pDict->nLists - 1; (i >= 0) && (!pFW); --i)
476 pHash = pDict->pSearch[i];
477 pFW = hashLookup(pHash, si, hashCode);
480 ficlLockDictionary(0);
486 /**************************************************************************
487 d i c t R e s e t S e a r c h O r d e r
488 ** Initialize the dictionary search order list to sane state
489 **************************************************************************/
490 void dictResetSearchOrder(FICL_DICT *pDict)
493 pDict->pCompile = pDict->pForthWords;
495 pDict->pSearch[0] = pDict->pForthWords;
500 /**************************************************************************
501 d i c t S e t F l a g s
502 ** Changes the flags field of the most recently defined word:
503 ** Set all bits that are ones in the set parameter, clear all bits
504 ** that are ones in the clr parameter. Clear wins in case the same bit
505 ** is set in both parameters.
506 **************************************************************************/
507 void dictSetFlags(FICL_DICT *pDict, UNS8 set, UNS8 clr)
509 assert(pDict->smudge);
510 pDict->smudge->flags |= set;
511 pDict->smudge->flags &= ~clr;
516 /**************************************************************************
517 d i c t S e t I m m e d i a t e
518 ** Set the most recently defined word as IMMEDIATE
519 **************************************************************************/
520 void dictSetImmediate(FICL_DICT *pDict)
522 assert(pDict->smudge);
523 pDict->smudge->flags |= FW_IMMEDIATE;
528 /**************************************************************************
529 d i c t U n s m u d g e
530 ** Completes the definition of a word by linking it
531 ** into the main list
532 **************************************************************************/
533 void dictUnsmudge(FICL_DICT *pDict)
535 FICL_WORD *pFW = pDict->smudge;
536 FICL_HASH *pHash = pDict->pCompile;
541 ** :noname words never get linked into the list...
544 hashInsertWord(pHash, pFW);
545 pFW->flags &= ~(FW_SMUDGE);
550 /**************************************************************************
552 ** Returns the value of the HERE pointer -- the address
553 ** of the next free cell in the dictionary
554 **************************************************************************/
555 CELL *dictWhere(FICL_DICT *pDict)
561 /**************************************************************************
563 ** Unlink all words in the hash that have addresses greater than or
564 ** equal to the address supplied. Implementation factor for FORGET
566 **************************************************************************/
567 void hashForget(FICL_HASH *pHash, void *where)
575 for (i = 0; i < pHash->size; i++)
577 pWord = pHash->table[i];
579 while ((void *)pWord >= where)
584 pHash->table[i] = pWord;
591 /**************************************************************************
592 h a s h H a s h C o d e
594 ** Generate a 16 bit hashcode from a character string using a rolling
595 ** shift and add stolen from PJ Weinberger of Bell Labs fame. Case folds
596 ** the name before hashing it...
597 ** N O T E : If string has zero length, returns zero.
598 **************************************************************************/
599 UNS16 hashHashCode(STRINGINFO si)
603 UNS16 code = (UNS16)si.count;
609 for (cp = (UNS8 *)si.cp; *cp && si.count; cp++, si.count--)
611 code = (UNS16)((code << 4) + tolower(*cp));
612 shift = (UNS16)(code & 0xf000);
615 code ^= (UNS16)(shift >> 8);
616 code ^= (UNS16)shift;
624 /**************************************************************************
625 h a s h I n s e r t W o r d
626 ** Put a word into the hash table using the word's hashcode as
627 ** an index (modulo the table size).
628 **************************************************************************/
629 void hashInsertWord(FICL_HASH *pHash, FICL_WORD *pFW)
636 if (pHash->size == 1)
638 pList = pHash->table;
642 pList = pHash->table + (pFW->hash % pHash->size);
651 /**************************************************************************
653 ** Find a name in the hash table given the hashcode and text of the name.
654 ** Returns the address of the corresponding FICL_WORD if found,
656 ** Note: outer loop on link field supports inheritance in wordlists.
657 ** It's not part of ANS Forth - ficl only. hashReset creates wordlists
658 ** with NULL link fields.
659 **************************************************************************/
660 FICL_WORD *hashLookup(FICL_HASH *pHash, STRINGINFO si, UNS16 hashCode)
662 FICL_COUNT nCmp = (FICL_COUNT)si.count;
666 if (nCmp > nFICLNAME)
669 for (; pHash != NULL; pHash = pHash->link)
672 hashIdx = (UNS16)(hashCode % pHash->size);
673 else /* avoid the modulo op for single threaded lists */
676 for (pFW = pHash->table[hashIdx]; pFW; pFW = pFW->link)
678 if ( (pFW->nName == si.count)
679 && (!strincmp(si.cp, pFW->name, nCmp)) )
682 assert(pFW != pFW->link);
691 /**************************************************************************
693 ** Initialize a FICL_HASH to empty state.
694 **************************************************************************/
695 void hashReset(FICL_HASH *pHash)
701 for (i = 0; i < pHash->size; i++)
703 pHash->table[i] = NULL;
710 /**************************************************************************
711 d i c t C h e c k T h r e s h o l d
712 ** Verify if an increase in the dictionary size is warranted, and do it if
714 **************************************************************************/
716 void dictCheckThreshold(FICL_DICT* dp)
718 if( dictCellsAvail(dp) < dictThreshold ) {
719 dp->dict = ficlMalloc( dictIncrease * sizeof (CELL) );
722 dp->size = dictIncrease;