{Note The next few paragraphs seem to be a little too explicit. Do we really want to go into this much detail? How about describing hash arrays as 'black-boxes' that you give a hash-item to and receive a hash-value in return? Perhaps there could be a seperate section later describing the algorithm in a little more detail. ---mjs} {index hash links} A hash link is an association between any Interlisp pointer (atoms, numbers, arrays, strings, lists, et al) called the {index hash-item}{term hash-item}, and any other Interlisp pointer called the {index hash-value}{term hash-value}. Hash links are implemented by computing an address, called the {term hash-address}{index hash-address}, in a specified array, called the {term hash-array},{index hash-array} and storing the {term hash-item}{index hash-item} and the {term hash-value}{index hash-value} into the cell with that address. The contents of that cell, i.e. the {term hash-item} and {term hash-value}, is then called the {index hash-link}{term hash-link}.{foot The term {term hash link} (unhyphenated) refers to the process of associating information this way, or the "association" as an abstract concept. }{comment endfootnote} Since the hash-array is obviously much smaller than the total number of possible hash-items,{foot which is the total number of Interlisp pointers, i.e. in Interlisp-10, 256K. }{comment endfootnote} the hash-address computed from {arg ITEM} may already contain a hash-link. If this link is from {arg ITEM},{foot {fn EQ} is used for comparing {arg ITEM} with the hash-item in the cell. }{comment endfootnote} the new hash-value simply replaces the old hash-value. Otherwise, another hash-address (in the same hash-array) must be computed, etc, until an empty cell is found,{foot When the hash array becomes 7/8 full, it is considered to be full, and the array is either enlarged, or an error is generated, as described below in the discussion of overflow. }{comment endfootnote} or a cell containing a hash-link from {arg ITEM}. {index hash links} When a hash link for {arg ITEM} is being retrieved, the hash-address is computed using the same algorithm as that employed for making the hash link. If the corresponding cell is empty, there is no hash link for {arg ITEM}. If it contains a {index hash-link}hash-link from {arg ITEM}, the {index hash-value}hash-value is returned. Otherwise, another hash-address must be computed, and so forth. Note that more than one hash link can be associated with a given {index hash-item}hash-item by using more than one {index hash-array}hash-array. ---------- {var SYSHASHARRAY} is not used by the system, it is provided solely for the user's benefit. It is initially 512 words large, and is automatically enlarged by 50% whenever it is "full". See page {PageRef L!6}. ----------- In Interlisp-10, the size of the hash array may be increased so that it is relatively prime to possible probe intervals.{note is this worth mentioning?} {Begin Note} Date: 28 Oct 1981 11:16 PST From: KAPLAN at PARC-MAXC FYI, hash arrays in Interlisp-D currently ARE a power of 2, in the interests of efficiency. That might change if we get microcode support for hashing. {End Note} ----------- By using an array argument of a special form, the user can provide for automatic enlargement of a {index hash-array}hash-array when it overflows, i.e., is full and an attempt is made to store a hash link into it. The array argument is either of the form [1] {lisp ({arg HARRAY} . {arg N})}, {arg N} a positive integer; [2] {lisp ({arg HARRAY} . {arg F})}, {arg F} a floating point number; [3] {lisp ({arg HARRAY})}; or [4] {lisp ({arg HARRAY} . {arg FN})}, {arg FN} a function name or a lambda expression. In the first case, a new hash-array is created with {arg N} more cells than the current hash-array. In the second case, the new hash array will be {arg F} times the size of the current hash-array. The third case, {lisp ({arg HARRAY})}, is equivalent to {lisp ({arg HARRAY} . 1.5)}. In the fourth case, {lisp ({arg HARRAY} . {arg FN})}, {arg FN} is called with {lisp ({arg HARRAY} . {arg FN})} as its argument. If {arg FN} returns a number, the number will be the size of the new hash array. Otherwise, the new size defaults to 1.5 times the size of the old hash array, e.g. {arg FN} could be used to print a message, or perform some monitor function. In each case, the new hash-array is {fn RPLACA}ed into the dotted pair, and the computation continues. If a {index hash-array}hash-array overflows, and the array argument used was not one of these three forms, the error {lisp HASH TABLE FULL}{index HASH TABLE FULL EM} is generated, which will either cause a break or unwind to the last {index ERRORSET}{fn ERRORSET}, as per treatment of errors described in {SectionRef L!ERRORS}. The system hash array,{index SYSHASHARRAY SY} {var SYSHASHARRAY}, is automatically enlarged by 1.5 when it is full. ----------- {Begin Note} Date: 23 Sep 1981 0740-PDT From: Dave Dyer <DDYER at USC-ISIB> To: Lispdiscussion↑.pa at PARC-MAXC I'd like opinions to what extent hash arrays should be usable interchangably with ordinary arrays. Should SETA and ELT work? Should both SETA and SETD retreve the key and data fields respectively? Should ARRAYSIZE work? And which number, actual size or HARRAYSIZE should it return if those are different? I tend to favor the view that ARRAYS and HARRAYS should not be interchangable, to allow maximum flexibility in the choice of the representation of hash arrays across various Interlisps. (in which case, Interlisp-10 should be made more restrictive) Date: 25 Sep 1981 10:09 PDT From: Bobrow at PARC-MAXC In-reply-to: DDYER's message of 23 Sep 1981 0740-PDT I agree that ARRAYS and HARRAYS should not be interchangable. The garbage collection properties of the two should be able to be different. Having something as a key in a hash array should not necessarily allow one to hold on to it. Date: 25 Sep 1981 14:17 PDT From: Masinter at PARC-MAXC Subject: Re: Arrays and hasha arrays The issue has very little to do with GC semantics, which relates as much to MAPHASH rather than ELT and SETA, but whether HARRAYP can be implemented other than as an array. My opinions are: HARRAYP can be a separate datatype from ARRAYP. All current lisp system code is believed to be written in such a way that that is true (it is stated so in the VM). Note that TYPENAME must return the appropriate token when given an ARRAYP or HARRAYP. Thus, ARRAYSIZE, SETA, ELT, SETD, ELTD are NOT expected to work on HARRAYPs; the only operations valid for things created by HARRAY are GETHASH, PUTHASH, CLRHASH, MAPHASH, HARRAYSIZE. The requirement on HARRAYSIZE is as follows: (HARRAYSIZE (HARRAY n)) ge n X must hold (HARRAYSIZE X) - (HARRAYSIZE Y) more key/value atributes than Y. (In Interlisp-D, while things created by HARRAYP will be ARRAYP and respond to ARRAYSIZE, the ELT and SETA functions will complain when given a HARRAYP.) {End Note} {Begin Note} Date: 19 Apr 1982 16:03 PST From: JonL at PARC-MAXC Subject: Re: COPYARRAY I agree pretty strongly with Dyer that "hash-arrays" should not be thought of as arrays -- I've a version of a hashing package written in NIL/CommonLisp which implements a HASH-TABLE as a semi-intelligent "object", and generally one array isn't enough to hold the appropriate data (about a dozen more state variables were needed, and in some cases a second data array). The functionalities implemented were a superset of those documented in the LISPMachine manual for hash-on-eq and hash-on-equal. Perhaps the current Interlisp hasharray should be kept for backwards compatibility only, and some future package could implement a more developed facility. If so, it would be fairly important to have a type of array which holds pointers, but which does not cause the pointers to be protected/copied during a GC; alternatively, there could be a type of pointer array which the GC sweeps after marking everything else, and just deletes entries which haven't been marked elsewhere. Thus it would be possible for "GC" methods to take care of the case where you want a table entry to go away when no one else points to its key. Incidentaly, the use of EXTENDs (i.e., "object-oriented" programming) made it easy to put in special PRINT methods for these hash-tables, but let the default class heirarchy values stand for most other methods, e.g., COPY. Date: 19 APR 1982 2246-PST From: MASINTER.PA It is clear that Interlisp says that HARRAYPs can be different from ARRAYP, and it is a quirk of Interlisp-10 that COPYARRA works (I'm actually not sure). But what is this about GC's "sweeping"? {End Note} {Begin Note} -------------------------------------the following few messages deal with the problem of hash array entries being deleted by the garbage collector -----mjs Date: 3 NOV 1977 1925-PST Date: 29 APR 1975 1115-PDT From: DEUTSCH Subject: GC To: hartley at BBNB cc: teitelman, bobrow I have (apparently ) finally encountered a situation where I really need to know under precisely what circumstances the G.C. will delete an entry from a hash array. I have a (unfortunately large) program which works properly if I do MINFS(25000) and RECLAIM) before I start running it, but if I don't, it blows up with a NON-NUMERIC ARG NIL which results from something which it "knows" is in a hash array not being there. In the latter case, a GC: 8 occurs during a phase of the computation when there are many hash arrays being filled which will be examined later. Just to explain my current understanding: it is my impression that the G.C. will delete an entry from a hash array if the key (the thing you give to GETHASH) is otherwise reclaimable, i.e. if there are no references to the key other than from entry keys in hash arrays, and the key is not a small number or litatom. If this is true, I think it may be what is screwing me, since I think I may have hash arrays which at some point in their lives only exist to be scanned with MAPHASH and whose keys no longer have references to them in some cases. (This is not a contrived situation: imagine a system for manipulating sets which represents sets as hash arrays. Then the keys almost certainly have no other references to them, and the ONLY important use of the arrays is MAPHASH.) ------- 29-APR-75 13:21:41-PDT,1050;000000000001 Net mail from site BBN-TENEXB rcvd at 29-APR-75 13:21:33 Date: 29 APR 1975 1619-EDT From: HARTLEY at BBN-TENEXB Subject: HASH ARRAYS AND GC To: DEUTSCH at PARC cc: TEITELMAN at PARC, BOBROW at PARC, LEWIS PETER, YOUR SITUATION IS A MORE COMPLICATED VERSION OF A SITUATION THAT AROSE HERE AWHILE AGO AND HAS BEEN BUGGING ME EVER SINCE. A USER SAVED A HASH ARRAY WITH DUMPHASH(SPELLING?) AND FOUND THAT SHE COULDNT GET IT LOADED IN BECAUSE A GC OCCURED WHILE IT WAS LOADING AND ALL THE HASH ENTRIES WENT AWAY. HER INTENT WAS TO LOAD THE HASH ARRAY AND THEN MAPHASH THRU IT TO CONSTRUCT THE REST OF HER DATA STRUCTURE (HAD SHE GOTTENT O THE STAGE OF CREATING THE REST OF THE STRUCTURE BEFORE THE GC THEN THE HASH ARRAY WOULD NOT HAVE DISAPPEARED (THE CONTENTS OF THE HASH ARRAY THET IS) ). THE LESSON TO BE LEARNED IS THAT EITHER MAPHASH SHOULD NOT EXIST, OR THAT THE GARBAGE COLLECTOR SHOULD NEVER DELETE ENTRIES FROM A HASH ARRAY. THIS PROBLEM IS RELATED TO MY OBJECTIONS TO THE DESIRE FOR MAPATOMS. Date: 29 APR 1975 1414-PDT From: DEUTSCH Subject: hash arrays & gc To: hartley at BBNB cc: teitelman, bobrow, lewis at BBNB Well, I'm glad to learn I'm not alone in being bitten by this bug. The logical problem is fairly subtle. I'm not willing to give up MAPHASH (and I see it as being different than MAPATOMS in that it requires an explicit act to create and delete hash associations whereas atoms are created "at need" and therefore may more reasonable be reclaimed "when no longer needed"). What is really going in is that some hash arrays are sort of like property list associations, which persist indefinitely even if the user has forgotten all about the atom that possesses them, whereas others are more like associations by direct pointers (like list-records), which disappear when the key is no longer accessible. Litatoms can possess both types of associations: an example of the former is the EXPR property, whereas a "memo function" like a numeric hash from the name might be an example of the latter property. I confess I don't see a good solution to this problem. The one that comes to mind is a bit in the hash array that tells the g.c. whether it is allowed to delete entries, but somehow this is unsatisfying. However, if (as it appears) the difference between the two situations is really the user's "intent", then there is no way the system can always do the right thing, and some kind of explicit user-settable bit is really required. Comments? -----------------------------------the following few messages deal with the problem of MAPHASH (in Interlisp-10) working incorrectly if a garbage collection occurs at the wrong time-----mjs Date: 1 Mar 1982 13:09 PST From: Masinter at PARC-MAXC Subject: Re: MAPHASH problems in Interlisp?? (1) You have unfortunately run into one of the more subtle problems with MAPHASH in Interlisp-10: if a garbage collection which MOVES STORAGE occurs during the middle of a MAPHASH, it is possible for the hash pointers to move around, and for entries to be missed and for some entries to be visitied twice. This is the only situation in which MAPHASH will omit items or present them twice (note that "rehashing" actually copies the original array into another one, so that if a rehash occurs because of overflow, you may get outdated information but not any duplicates.) (2) The problem is that if a reclaim needs to increase the size of one of the contiguous areas (such as array space or string pointer space), it may actually move around pages of atoms. It isn't that atoms get compacted but rather that other spaces have to increase which causes the atoms to get moved around. (3) The way that I worked around this problem when I ran into it was (a) MAPHASH down the array, collecting a list of the "keys" (b) MAPC down that list, performing the operation This guarantees that no string/array/pname garbage collection will occur during MAPHASH. There are some proposals for fixing this problem in Interlisp-10 (e.g., marking the array that it is being maphashed, and if so marked, not rehashing during a reclaim but fixing it the next puthash) but so far (for the last 4 years) no progress on fixing it. This bug is not present in other Interlisp implementations, as far as I know. Date: 1 Mar 1982 1732-PST From: Steve Crocker <Crocker at USC-ISIF> Subject: Re: MAPHASH problems in Interlisp?? An alternative strategy is not to use MAPHASH at all. An auxiliary list may be kept, suitably updated whenever items are added to or deleted from the table. If this sounds ridiculously expensive, I submit that it competes with the proposed solution for some frequencies of MAPHASHing, adding, deleting and accessing elements. It would be interesting to see the crossover point. {End Note}