(FILECREATED "18-SEP-79 09:21:49" <LISPUSERS>HASH.;26 57643  

     changes to:  COPYHASHFILE

     previous date: "18-SEP-79 08:26:13" <LISPUSERS>HASH.;25)


(PRETTYCOMPRINT HASHCOMS)

(RPAQQ HASHCOMS ((FNS CLOSEHASHFILE COLLECTPAGES COPYCHARSI COPYCHARSV COPYHASHFILE COPYHASHPAGES 
		      CREATEHASHFILE DELPAGE GET#SLOTS GETFILEPAGE GETHASHFILE GETPAGE GETPNAME 
		      GETVALUE HASHAFTERCLOSE HASHFILENAME HASHFILEP HASHFILEPROP HASHFILESPLST 
		      HASHFILESPLST1 HASHSTATUS HFP INITHASHPAGE LOOKUPHASHFILE LOOKUPHASHFILE1 
		      MAPHASHFILE NEWDIRSLOTS OPENHASHFILE POW2 PREPVALUE PRINTREGION PUTHASHFILE 
		      PUTVALUE REHASHFILE REHASHPAGE REHASHPAGES SCANHASHFILE VALUEBOX VALUETYPENUM)
	(DECLARE: EVAL@COMPILE DONTCOPY (PROP MACRO * HASHMACROS)
		  (TEMPLATES * HASHMACROS)
		  (RECORDS HashFile Slot Page DirSlot DirPage HASHFILESPLST SYMBOL FILEHANDLE)
		  (VARS * VALUETYPES)
		  (VARS * CALLTYPES)
		  (VARS (WRITETYPES (LOGOR INSERT REPLACE DELETE))
			(WordsPerPage 512)
			(WordBits 9)
			(WordMask (WordsPerPage-1))
			(BitsPerWord 36)
			(BitsPerChar 7)
			(CharMask (2^BitsPerChar-1))
			(CharsPerWord BitsPerWord/BitsPerChar)
			(CharsPerPage CharsPerWord*WordsPerPage)
			(MaxCharLocation CharsPerPage-1)
			(MaxStringLength 127)
			(#InitialWordsOnPage 1)
			(DefaultKeySize 5)
			(DefaultInitial#Pages 2)
			(PrintMargin 128))
		  (FILES (SYSLOAD FROM VALUEOF LISPUSERSDIRECTORIES)
			 NOBOX)
		  (P (RESETSAVE DWIMIFYCOMPFLG T)
		     (CLISPDEC (QUOTE FAST))
		     (SETQ FONTFNS HASHMACROS))
		  (I.S.OPRS inpage)
		  (ALISTS (PRETTYEQUIVLST SELVALTYPEQ)
			  (DWIMEQUIVLST SELVALTYPEQ)))
	(BLOCKS * HASHBLOCKS)
	[VARS (SYSHASHFILE)
	      (HASHFILESPLST)
	      (SYSHASHFILEARRAY (CONS (HARRAY 10]
	(ADDVARS (HASHFILECHCONLST))))
(DEFINEQ

(CLOSEHASHFILE
  [LAMBDA (HASHFILE)                                   (* rmk: "27-JAN-78 18:01" posted: "13-DEC-77 09:21")
    (CLOSEF (HASHFILENAME HASHFILE])

(COLLECTPAGES
  [LAMBDA (HFILE)                                      (* rmk: "23-SEP-78 22:25")
                                                       (* Makes a list of all page#'s from the HFILE directory)
    (bind P (DIRPAGE _(GETDIRPAGE HFILE)) for I from 0 to 511 by (LLSH 1 P:Depth)
       collect (fetch Page# of (P_(WORDOFFSET DIRPAGE I])

(COPYCHARSI
  [LAMBDA (PGE CHARPOS)                                (* rmk: "12-JUN-77 16:13")

          (* Called by PUTINDEX in LOOKUPHASHFILE1. Copies characters from INDEXPTR to a "string" beginning at CHARPOS on PGE.
	  INDEXPTR and STRINGLENGTH are set up by PREPKEY. CHARPOS is a PGE-relative byte position. STRINGLENGTH will be 
	  stored in the first byte of CHARPOS, so the string is actually STRINGLENGTH+1 bytes long.)


    (ASSEMBLE NIL
	      (CQ STRINGLENGTH)
	      (SUBI 1 , ASZ)
	      (MOVE 5 , 1)                             (* Actually, should load this directly in 5)
	      (CQ (VAG (BYTEPOINT PGE CHARPOS BitsPerChar)))
                                                       (* The destination byte pointer)
	      (IDPB 5 , 1)                             (* Place the stringlength in the string)
	      (CQ2 INDEXPTR)
	      (MOVE 2 , 0 (2))
	  LP  (ILDB 3 , 2)
	      (IDPB 3 , 1)
	      (SOJG 5 , LP])

(COPYCHARSV
  [LAMBDA (PGE CHARPOS)                                (* rmk: "26-MAY-77 20:30")

          (* Called by PUTVALUE in LOOKUPHASHFILE1. Copies characters from INDEXPTR to a "string" beginning at CHARPOS on PGE.
	  INDEXPTR and STRINGLENGTH are set up by PREPKEY. Unlike ICOPYCHAR, it assumes that the string length is being saved 
	  somewhere esle.)


    (ASSEMBLE NIL
	      (CQ (VAG (BYTEPOINT PGE CHARPOS BitsPerChar)))
	      (CQ2 INDEXPTR)
	      (MOVE 4 , 0 (2))
	      (CQ2 STRINGLENGTH)
	      (SUBI 2 , ASZ)
	  LP  (ILDB 3 , 4)
	      (IDPB 3 , 1)
	      (SOJG 2 , LP])

(COPYHASHFILE
  [LAMBDA (HASHFILE NEWNAME FN VTYPE LEAVEOPEN)        (* rmk: "18-SEP-79 09:21")

          (* Copy (by rehashing) HASHFILE into NEWNAME. If FN is given, is applied to the value of each key, the old hashfile, and the new 
	  hashfile, and the value returned is used as the value of the key in the new hashfile. THis permits, e.g. copying strings or other 
	  structures. If VTYPE is given as well as FN, then the function will be applied to a value extracted according to the VTYPE, and the new 
	  value will be put in in that mode. This means that the user can coerce EXPR to NUMBER, e.g. However, the valuetype of the resulting file
	  will always be the same as the original file)


    (RESETLST (PROG (CVT NEWHASHFILE PAGES OLDVT)
		    (if CVT_(HFP HASHFILE)
			then HASHFILE_CVT
		      elseif HASHFILE_(OPENHASHFILE HASHFILE 'INPUT)
			then (RESETSAVE NIL <'CLOSEF? HASHFILE:File>)
		      else (RETURN))
		    (CVT_(SCANHASHFILE PAGES_(COLLECTPAGES HASHFILE)))
                                                       (* Find what's in use -
						       (#slots . avg.used))
		    [RESETSAVE (fetch File of (NEWHASHFILE_(CREATEHASHFILE (OR NEWNAME
									       (NAMEFIELD 
										    HASHFILE:File T))
									   (HASHFILEPROP HASHFILE 
										       'VALUETYPE)
									   CVT::1 1.2*CVT:1)))
			       (if LEAVEOPEN
				   then '(AND RESETSTATE (DELFILE (CLOSEF? OLDVALUE)))
				 else '(PROGN (CLOSEF? OLDVALUE)
					      (AND RESETSTATE (DELFILE OLDVALUE]
		    (OLDVT_ HASHFILE:HValueType)
		    (CVT_(if VTYPE
			     then (VALUETYPENUM VTYPE)
			   else OLDVT))
		    (if (if FN
			    then OLDVT~=CVT
			  elseif OLDVT=(CONSTANT SMALLEXPR)
			    then CVT_(CONSTANT STRING)
			  elseif OLDVT=(CONSTANT EXPR)
			    then CVT_(CONSTANT NUMBER)
			  elseif OLDVT=(CONSTANT SYMBOLTABLE)
			    then (ERROR "A copying function is required for SYMBOLTABLE files"))
			then (HASHFILE_(create HashFile using HASHFILE HValueType _ CVT ValueBox _(
								VALUEBOX CVT)))
			     (HASHFILE:Tag_HASHFILE) 
                                                       (* NEWHASHFILE needs the correct valubox for rehashing)
			     (NEWHASHFILE_(create HashFile
					     using NEWHASHFILE HValueType _ CVT ValueBox _(VALUEBOX
						     CVT)))
			     (NEWHASHFILE:Tag_NEWHASHFILE))
		    (COPYHASHPAGES PAGES HASHFILE NEWHASHFILE FN OLDVT=(CONSTANT EXPR))
		    (RETURN NEWHASHFILE:File])

(COPYHASHPAGES
  [LAMBDA (PAGES HASHFILE NEWHASHFILE FN EXPRFLAG)     (* rmk: "25-APR-79 12:09")
                                                       (* Rehashes PAGES of HASHFILE into NEWHASHFILE)
    (RESETLST (RPTQ 4 (ADDMAPBUFFER T))                (* We need to lock one page, and it helps to keep the directory and the
						       2 new pages in core.)
	      (for P# PPTR VALUE (OLDFILE _ HASHFILE:File)
		   (NEWFILE _ NEWHASHFILE:File) inside PAGES
		 do (RESETLST (RESETSAVE (LOCKMAP PPTR_(MAPPAGE P# OLDFILE))
					 '(PROGN (UNLOCKMAP OLDVALUE)))
			      (inpage PPTR do (VALUE_(GETVALUE))
					      (if FN
						  then VALUE_(APPLY* FN VALUE HASHFILE NEWHASHFILE)
						elseif EXPRFLAG
						  then VALUE_(PROG1
							 (IBOX (GETFILEPTR NEWFILE))
                                                       (* Copy value by hand and return the fileptr)
							 (SETFILEPTR OLDFILE VALUE)
							 (SKREAD OLDFILE)
							 (COPYBYTES OLDFILE NEWFILE VALUE
								    (IBOX (GETFILEPTR OLDFILE)))
							 (TERPRI NEWFILE)))
					      (LOOKUPHASHFILE1 (GETINDEX SLOT PAGE)
							       VALUE NEWHASHFILE (CONSTANT INSERT])

(CREATEHASHFILE
  [LAMBDA (FILE VALUETYPE ITEMLENGTH #ENTRIES)         (* rmk: "18-SEP-79 08:25" posted: "13-DEC-77 09:21")
    (if (AND (LISTP ITEMLENGTH)
	     #ENTRIES=NIL)
	then (#ENTRIES_ITEMLENGTH:2)
	     (ITEMLENGTH_ITEMLENGTH:1))
    (RESETLST (PROG (HASHFILE TEMP DIRPAGE)
		    (if ~FILE
			then (RETURN))
		    (VALUETYPE_(VALUETYPENUM VALUETYPE))
		    [RESETSAVE FILE_(IOFILE (CLOSEF (OPENFILE FILE 'OUTPUT 'NEW)))
			       '(AND RESETSTATE (PROGN (CLOSEF? OLDVALUE)
						       (DELFILE OLDVALUE]
                                                       (* Defer file opening until after the value checking, so we don't have 
						       to close on error.)
		    (HASHFILE_(create HashFile
				      Access _('BOTH)
				      HValueType _ VALUETYPE
				      File _ FILE
				      FileJfn _(OPNJFN FILE)
				      ValueBox _(VALUEBOX VALUETYPE)
				      HWordsPerSlot _(if VALUETYPE=(CONSTANT 2NUMBERS)
							 then 2
						       else 1)))
                                                       (* Each slot has 1 word in the basic slot region, 2NUMBERS files have 
						       another word in the adjacent extension region)
		    (DIRPAGE_(GETDIRPAGE HASHFILE))
		    (DIRPAGE:ValueType_VALUETYPE)
		    (NEWDIRSLOTS DIRPAGE 0 9 TEMP_(GET#SLOTS (if (AND (NUMBERP ITEMLENGTH)
								      (ITEMLENGTH gt 0))
								 then ITEMLENGTH
							       elseif (TESTBITS VALUETYPE 
										STRING/SMALLEXPR)
								 then (CONSTANT DefaultKeySize*2)
							       else (CONSTANT DefaultKeySize)))
				 (PROG1 TEMP_(if (AND (NUMBERP #ENTRIES)
						      (#ENTRIES gt 0))
						 then #ENTRIES/TEMP+1
					       else (CONSTANT DefaultInitial#Pages))
					(PRINTREGION TEMP+1)))
		    (PUTDIRPAGE DIRPAGE HASHFILE)
		    (HASHFILE:Tag_HASHFILE)
		    (if (AND VALUETYPE=(CONSTANT SMALLEXPR)
			     (NLISTP HASHFILECHCONLST))
			then HASHFILECHCONLST_(to 128 collect NIL))
                                                       (* Generate the scratchlist only to write on SMALLEXPR files)
		    (if (AND (TESTBITS VALUETYPE SMALLEXPR EXPR)
			     ~(READTABLEP (EVALV 'HASHFILERDTBL)))
			then HASHFILERDTBL_(COPYREADTABLE 'ORIG))
		    (PUTHASH FILE HASHFILE SYSHASHFILEARRAY)
		    (WHENCLOSE FILE 'AFTER 'HASHAFTERCLOSE)
		    (RETURN HASHFILE])

(DELPAGE
  [LAMBDA (PAGE# HASHFILE)                             (* rmk: " 5-SEP-78 23:01")
                                                       (* An entry for the user who wants to give back one of his pages in 
						       HASHFILE)
    HASHFILE_(HFP HASHFILE T NIL T)
    (CLEARMAP HASHFILE:File PAGE#)                     (* Make sure the page is unmapped)
    (DELFILEPAGE HASHFILE PAGE#])

(GET#SLOTS
  [LAMBDA (ITEMLENGTH)                                 (* rmk: "22-MAY-79 15:31")

          (* #Slots per page is chosen to be prime, and such that the character table and slot-table will fill at the same time, if ITEMLENGTH is 
	  the average number of characters occupied per slot. A prime #Slots simplifies computing the reprobing interval when hashing.
	  -
	  Note that if the number of addressable characters per page falls below the number of actual characters per page, the that 
	  non-addressable space should be included in the slot region. Currently, with 2560 chars and 12 bit addresses, we don't have to worry 
	  about this.)

                                                       (* Add1 to ITEMLENGTH cause the index-length byte isn't included)
    (FIND1STPRIME (FIX (FQUOTIENT (CONSTANT WordsPerPage-#InitialWordsOnPage)
				  (FPLUS HASHFILE:HWordsPerSlot (FQUOTIENT ITEMLENGTH+1
									   (CONSTANT CharsPerWord])

(GETFILEPAGE
  [LAMBDA (N)                                          (* rmk: " 1-JAN-79 22:44")
    (PROG [UP (FP (LOGAND 262143 (JSYS 25 HASHFILE:FileJfn]
                                                       (* The FFFP JSYS. Returns page# in rh of 1)
      RETRY
          (if FP gt HASHFILE:PrintPage#
	      then (FP_(LOWESTFREEPAGE FP HASHFILE:FileJfn))
		   (PRINTREGION (if N
				    then N+FP
				  else FP))
	    elseif N=NIL
	    elseif ~(UP_(USEDPAGE FP HASHFILE:FileJfn))
	    elseif UP-FP lt N
	      then                                     (* The next usedpage above FP, UP, is not at least N away.
						       Find a higher candidate FP.)
		   (FP_(NEXTFREEPAGE UP HASHFILE:FileJfn))
		   (GO RETRY))
          (RETURN FP])

(GETHASHFILE
  [LAMBDA (KEY HASHFILE)                               (* rmk: " 5-SEP-78 22:45" posted: "13-DEC-77 09:23")
    (LOOKUPHASHFILE1 KEY NIL (HFP HASHFILE NIL NIL T)
		     (CONSTANT RETRIEVE])

(GETPAGE
  [LAMBDA (HASHFILE N)                                 (* rmk: "10-SEP-78 01:52")
                                                       (* Returns the page number of a virgin page in HASHFILE.
						       Available to a user who wants non-hash pages in the file)
    HASHFILE_(HFP HASHFILE T NIL T)
    (GETFILEPAGE N])

(GETPNAME
  [LAMBDA (FILEADR HASHFILE)                           (* rmk: "23-SEP-78 22:25")

          (* Returns a temporary string pointer to the PNAME of FILEADR. Returns NIL if the PNAME pointer is 0, causes an error if it is not zero 
	  but points somewhere on the directory page.)


    HASHFILE_(HFP HASHFILE NIL (CONSTANT SYMBOLTABLE)
		  T)
    (PROG (PGE IP# (SLT (HLOCATE (OR (SMALLP FILEADR)
				     (IBOX FILEADR:FileAddress))
				 HASHFILE)))
          (if (IEQP 0 SLT:IndexPointer)
	      then (RETURN NIL)
	    elseif (IEQP 0 IP#_SLT:IndexPage#)
	      then (ERROR "Invalid name pointer for symbol" FILEADR)
	    else (SLT_(WORDOFFSET PGE_(HMAPIN IP# HASHFILE)
				  SLT:IndexWord#))
		 (RETURN (GETINDEX SLT PGE])

(GETVALUE
  [LAMBDA NIL                                          (* rmk: "25-APR-79 16:36")
    (DECLARE (USEDFREE HASHFILERDTBL SLOT HASHFILE PAGE))
    (SELVALTYPEQ HASHFILE:HValueType
		 ((NUMBER SYMBOLTABLE)
		                                       (* This is a replace-I that returns the pointer to the smashed box.)
		   (FREPLACEFIELDVAL 608174080 HASHFILE:ValueBox SLOT:Value))
		 (STRING (SMASHSTRINGPOINTERV SLOT:VLength SLOT:VCharPos PAGE HASHFILE:ValueBox))
		 (SMALLEXPR (READ (SMASHSTRINGPOINTERV SLOT:VLength SLOT:VCharPos PAGE
						       (CONSTANT (CONCAT)))
				  HASHFILERDTBL))
		 (EXPR (SETFILEPTR HASHFILE:File (IBOX SLOT:Value))
		       (READ HASHFILE:File HASHFILERDTBL))
		 (2NUMBERS HASHFILE:ValueBox:1:I_SLOT:Value HASHFILE:ValueBox::1:I_
			   (fetch I of (WORDOFFSET SLOT PAGE:#Slots))
			   HASHFILE:ValueBox)
		 (HELP 'GETVALUE])

(HASHAFTERCLOSE
  [LAMBDA (FILE)                                       (* rmk: " 5-SEP-78 23:00" posted: "13-DEC-77 09:21")
    (PROG (HASHFILE)
          (if HASHFILE_(HFP FILE)
	      then (if HASHFILE=SYSHASHFILE
		       then SYSHASHFILE_NIL)
		   (if (AND (ARRAYP HASHFILESPLST)
			    HASHFILE=HASHFILESPLST:HASHFILE)
		       then HASHFILESPLST_NIL)
		   (PUTHASH FILE NIL SYSHASHFILEARRAY) 
                                                       (* Remove from table of open hash files, and mark this datum defunct)
		   (HASHFILE:Tag_NIL)
		   (HASHFILE:FileJfn_-1])

(HASHFILENAME
  [LAMBDA (HASHFILE)                                   (* rmk: " 5-SEP-78 22:45")
    (fetch File of (HFP HASHFILE NIL NIL T])

(HASHFILEP
  [LAMBDA (X)                                          (* rmk: " 7-SEP-78 19:08")
                                                       (* The user predicate for hashfile-hood)
    (HFP X])

(HASHFILEPROP
  [LAMBDA (HASHFILE PROP)                              (* rmk: "18-SEP-79 08:23")
                                                       (* Returns the value of property PROP of HASHFILE.
						       A user entry)
    HASHFILE_(HFP HASHFILE NIL NIL T)
    (SELECTQ PROP
	     (NAME HASHFILE:File)
	     (VALUETYPE (SELVALTYPEQ HASHFILE:HValueType
				     (NUMBER 'NUMBER)
				     (STRING 'STRING)
				     (SMALLEXPR 'SMALLEXPR)
				     (EXPR 'EXPR)
				     (SYMBOLTABLE 'SYMBOLTABLE)
				     (2NUMBERS '2NUMBERS)
				     (SHOULDNT)))
	     (ACCESS HASHFILE:Access)
	     [#ENTRIES (IPLUS (CAR (SCANHASHFILE (COLLECTPAGES HASHFILE]
	     [ITEMLENGTH (FPLUS (CDR (SCANHASHFILE (COLLECTPAGES HASHFILE]
	     (SPACE (PROG [(X (SCANHASHFILE (COLLECTPAGES HASHFILE]
                                                       (* This is the order that can be given directly to CREATEHASHFILE)
		          (RETURN <(FPLUS X::1)
				    (IPLUS X:1)
				    >)))
	     (ERRORX <27 PROP>])

(HASHFILESPLST
  [LAMBDA (HASHFILE)                                   (* rmk: " 5-SEP-78 23:01")
    HASHFILE_(HFP HASHFILE NIL NIL T)
    (if (ARRAYP HASHFILESPLST)
	then (HASHFILESPLST:INIT_NIL)
      else HASHFILESPLST_(create HASHFILESPLST))
    HASHFILESPLST:FN_'HASHFILESPLST1
    HASHFILESPLST:HASHFILE_HASHFILE
    HASHFILESPLST])

(HASHFILESPLST1
  [LAMBDA (ARRAY)                                      (* rmk: "25-APR-79 16:02")
                                                       (* ARRAY is used to keep state info -
						       PAGE and SLOT. LASTSLOT is the last slot on current page)

          (* * * NOTE: Assumes that PGE remains in core between calls. That's true, provided the spelling corrector predicate doesn't cause 
	  MAPPAGE's.)


    (PROG (PGE SLT)
          (if ARRAY:INIT
	      then                                     (* already initialized)
		   (PGE_ARRAY:PAGE)
		   (SLT_ARRAY:SLOT)
		   (GO NEXTSLOT))
          (if ~(ARRAY:PAGELST_(COLLECTPAGES ARRAY:HASHFILE))
	      then                                     (* Flatten the tree so we can map over it)
		   (RETURN))
          (ARRAY:INIT_T)
      PAGE(ARRAY:PAGE_PGE_(HMAPIN ARRAY:PAGELST:1 ARRAY:HASHFILE))
          (ARRAY:LASTSLOT_(GETSLOT PGE PGE:#Slots-1))
          (SLT_(GETSLOT PGE 0))
      SLOT(if (INUSE? SLT)
	      then (ARRAY:SLOT_SLT)                    (* Return string pointer to next key)
		   (RETURN (GETINDEX SLT PGE)))
      NEXTSLOT
          (if SLT~=ARRAY:LASTSLOT
	      then                                     (* Bump SLOT pointer and go to next slot)
		   (SLT_(WORDOFFSET SLT 1))            (* This is the increment for just the basic slot)
		   (GO SLOT)
	    elseif (ARRAY:PAGELST_ARRAY:PAGELST::1)
	      then                                     (* got next page of file)
		   (GO PAGE)
	    else                                       (* no more pages, return NIL to show we're done)
		 (ARRAY:INIT_NIL)
		 (RETURN])

(HASHSTATUS
  [LAMBDA (FILE HASHFILE PERMARGS)                     (* rmk: " 5-SEP-78 22:59")

          (* A WHENCLOSE-RESTORE function for hashfiles. Called with HASHFILE=NIL before sysouts, HASHFILE a hashfile datum afterwards.
	  Makes sure the file is reopened and that appropriate pages are mapped in)


    (if HASHFILE
	then (if (APPLY (FUNCTION PERMSTATUS)
			PERMARGS)
		 then (HASHFILE:FileJfn_(OPNJFN FILE)))
      else <'HASHSTATUS FILE (HFP FILE)
	     (PERMSTATUS FILE)::1>])

(HFP
  [LAMBDA (HFILE WRITE VALUETYPE ERRORIFNOT)           (* rmk: " 7-SEP-78 23:30")
                                                       (* The internal predicate for hashfile-hood)
    (PROG ((MSG "Not a hashfile"))
      LP  (if HFILE
	      then (SELECTQ (NTYP HFILE)
			    (1)
			    (12 HFILE_(OR (GETHASH HFILE SYSHASHFILEARRAY)
					  (GETHASH (OPENP HFILE)
						   SYSHASHFILEARRAY)
					  (GO ERROR)))
			    (GO ERROR))
	    elseif HFILE_SYSHASHFILE
	    else (GO ERROR))
          (if [AND HFILE=HFILE:Tag (OR ~VALUETYPE VALUETYPE=HFILE:HValueType
				       (PROGN MSG_'"Wrong value type" (GO ERROR)))
		   (OR ~WRITE HFILE:Access='BOTH (PROGN MSG_'"Not open for write" (GO ERROR]
	      then (RETURN HFILE))
      ERROR
          (if ERRORIFNOT
	      then (HFILE_(ERROR MSG HFILE))
		   (GO LP)
	    else (RETURN NIL])

(INITHASHPAGE
  [LAMBDA (#SLOTS)                                     (* rmk: "25-APR-79 16:04")
                                                       (* Allocates a hash page in HASHFILE.
						       Maps the page in, initializes it, and returns its page number.)
    (PROG (NEWPAGE (PAGE# (GETFILEPAGE)))
          (NEWPAGE_(HMAPIN PAGE# HASHFILE))
          (NEWPAGE:#Slots_#SLOTS)
          (NEWPAGE:FirstFree_(CONSTANT CharsPerWord)*((CONSTANT #InitialWordsOnPage)
	     +#SLOTS*HASHFILE:HWordsPerSlot))
          (RETURN PAGE#])

(LOOKUPHASHFILE
  [LAMBDA (INDEX VALUE HASHFILE CALLTYPE)              (* rmk: "26-APR-79 09:59")
    CALLTYPE_(for C $$VAL_0 inside CALLTYPE do $$VAL_(LOGOR $$VAL
							    (SELECTQ C
								     (INSERT (CONSTANT INSERT))
								     (REPLACE (CONSTANT REPLACE))
								     (DELETE (CONSTANT DELETE))
								     (RETRIEVE (CONSTANT RETRIEVE))
								     0)))
    (LOOKUPHASHFILE1 INDEX VALUE (HFP HASHFILE (TESTBITS CALLTYPE WRITETYPES)
				      NIL T)
		     CALLTYPE])

(LOOKUPHASHFILE1
  [LAMBDA (INDEX VAL HASHFILE CALLTYPE)                (* rmk: "26-APR-79 09:55")
    (PROG (STRINGLENGTH PAGE REALPAGE# SLOT# SLOTSTART DELETEDSLOT SLOT SLOTINC OLDVAL HASHKEY
			(INDEXPTR (IBOX)))             (* INDEXPTR is bound to a large number, as it is SETN'd by PREPEKY in 
						       COMPUTEHASHKEY)
      RESTART
          (HASHKEY_(COMPUTEHASHKEY INDEX))             (* sets INDEXPTR, STRINGLENGTH and sets HASHKEY to hash of INDEX)

          (* Now map in the page selected by HASHKEY (as determined by PICKPAGE), select SLOTSTART, and put the rest of the bits back in HASHKEY 
	  to use for selecting the reprobe interval)


          [SLOT#_SLOTSTART_(KEYDIV (fetch #Slots of (PAGE_(HMAPIN REALPAGE#_(PICKPAGE)
								  HASHFILE]
          (if SLOTINC_(KEYDIV PAGE:#Slots)=0
	      then SLOTINC_1)                          (* SLOTINC is the reprobe interval. Since #Slots per page is always 
						       prime, this number is easily selected)
      SLOT(if (EMPTY? SLOT_(GETSLOT PAGE SLOT#))
	      then (if (TESTBITS CALLTYPE INSERT)
		       then (if DELETEDSLOT
				then                   (* We found this deleted slot earlier in the search, so reuse it)
				     (SLOT_DELETEDSLOT))
			    (if (PUTINDEX)=NIL
				then (GO REHASH)
			      elseif (PUTVALUE)=NIL
				then (MAKEDELETED SLOT)
				     (GO REHASH)))
		   (RETURN)
	    elseif (DELETED? SLOT)
	      then (if ~DELETEDSLOT
		       then                            (* Save this slot in case we want to insert new INDEX)
			    (DELETEDSLOT_SLOT))
	    elseif (MATCHINDEX)
	      then                                     (* found INDEX)
		   (OLDVAL_(OR ~(TESTBITS CALLTYPE RETRIEVE)
			       (GETVALUE)))
		   (if (TESTBITS CALLTYPE DELETE)
		       then (MAKEDELETED SLOT)
			    (if HASHFILE:HValueType=(CONSTANT SYMBOLTABLE)
				then (replace IndexPointer of (HLOCATE SLOT:Value HASHFILE)
					with 0))       (* Smash the name pointer in the symbol.
						       Note that we can map pages, cause we're done with everything for this 
						       call.)
			    
		     elseif (TESTBITS CALLTYPE REPLACE)
		       then (if ~(PUTVALUE T)
				then (GO REHASH)))
		   (RETURN OLDVAL))
      RESLOT
          (SLOT#_(IREMAINDER SLOT#+SLOTINC PAGE:#Slots))
          (if SLOT#~=SLOTSTART
	      then (GO SLOT)
	    elseif ~(TESTBITS CALLTYPE INSERT)
	      then 

          (* if this page is full, and the item is not found on it, then it isn't in the table. Unless inserting, we can return NIL)


		   (RETURN))
      REHASH
          (if (TESTBITS CALLTYPE REHASH)
	      then (HELP "Attempt to Rehash during Rehashing!!"))
          (REHASHPAGE (LOGAND (COMPUTEHASHKEY INDEX)
			      (CONSTANT WordMask)))    (* Note: Can't keep the hashkey across rehashing, since some of the 
						       number boxes will be smashed, must be restored.)
          (GO RESTART])

(MAPHASHFILE
  [LAMBDA (HASHFILE FN)                                (* rmk: " 2-NOV-78 22:30")
    HASHFILE_(HFP HASHFILE NIL NIL T)
    (RESETLST (ADDMAPBUFFER T)
	      (ADDMAPBUFFER T)                         (* Prepare to lock a page in in case FN calls another hashfile 
						       function)
	      (for P# PPTR (FULL_(1 lt (NARGS FN))) in (COLLECTPAGES HASHFILE)
		 do (RESETLST (RESETSAVE (LOCKMAP PPTR_(HMAPIN P# HASHFILE))
					 '(PROGN (UNLOCKMAP OLDVALUE)))
			      (inpage PPTR do          (* If FULL is NIL, caller only wants key, so don't bother computing 
						       value)
					      (APPLY* FN (GETINDEX SLOT PAGE)
						      (AND FULL (GETVALUE])

(NEWDIRSLOTS
  [LAMBDA (DIRPAGE DSLOT# DEPTH #SLOTS #PAGES)         (* rmk: "29-SEP-78 23:48")

          (* Fills in new directory slots for the part of the tree around DSLOT#, which is currently at level DEPTH. Divides the interval into 
	  portions according to #PAGES.)


    (if DEPTH=0
	then (ERROR "Hashfile full -- can't expand!" HASHFILE:File))
    (RESETSAVE (LOCKMAP DIRPAGE)
	       <'UNLOCKMAP DIRPAGE>)                   (* Directory must be locked during INITHASHPAGE)
    (bind LEN LIM (INFO _(IBOX))
	  (NEWDEPTH_DEPTH-(POW2 #PAGES))
	  (BASE _(WORDOFFSET DIRPAGE (LLSH (LRSH DSLOT# DEPTH)
					   DEPTH)))
	  (J_(LLSH 1 DEPTH)+ -1) until J lt 0 first (if (MINUSP NEWDEPTH)
							then NEWDEPTH_0)
						    (LEN_(LLSH 1 NEWDEPTH))
						    (replace Depth of INFO with NEWDEPTH)
       do (LIM_J-LEN)
	  (replace Page# of INFO with (INITHASHPAGE #SLOTS))
	  (until J=LIM do (replace ReHashInfo of (WORDOFFSET BASE J) with INFO:I)
			  (J_J-1])

(OPENHASHFILE
  [LAMBDA (FILE ACCESS)                                (* rmk: "22-MAY-79 17:08" posted: "13-DEC-77 09:21")
    (PROG (HASHFILE VALUETYPE DIRPAGE)
          (if ~FILE
	      then (RETURN))
          (SELECTQ ACCESS
		   ((BOTH T)
		     (if HASHFILE_(HFP FILE)
			 then (if HASHFILE:Access='BOTH
				  then (RETURN HASHFILE)
				else                   (* File is not open for write, but we can clear out and reopen it that 
						       way)
				     (RETURN (OPENHASHFILE (CLOSEF HASHFILE:File)
							   'BOTH)))
		       else (FILE_(IOFILE FILE))
			    (ACCESS_'BOTH)))
		   ((INPUT NIL)
		     (if HASHFILE_(HFP FILE)
			 then (RETURN HASHFILE)
		       else (FILE_(OPENFILE FILE 'INPUT 'OLD))
			    (ACCESS_'INPUT)))
		   (ERRORX <27 ACCESS>))
          (HASHFILE_(create HashFile
			    File _ FILE
			    Access _ ACCESS
			    FileJfn _(OPNJFN FILE)))
          (DIRPAGE_(GETDIRPAGE HASHFILE))
          (SELVALTYPEQ VALUETYPE_DIRPAGE:ValueType
		       ((NUMBER EXPR SMALLEXPR SYMBOLTABLE 2NUMBERS STRING))
		       (PROGN (CLOSEF FILE)
			      (ERROR FILE "not a hashfile!")))
          (HASHFILE:HValueType_VALUETYPE)
          (HASHFILE:ValueBox_(VALUEBOX VALUETYPE))
          (HASHFILE:HWordsPerSlot_(if VALUETYPE=(CONSTANT 2NUMBERS)
				      then 2
				    else 1))
          (replace PrintPage# of HASHFILE with (GETEOFPTR FILE)/(CONSTANT CharsPerPage))
          (HASHFILE:Tag_HASHFILE)
          (if (AND HASHFILE:Access='BOTH VALUETYPE=(CONSTANT SMALLEXPR)
		   (NLISTP HASHFILECHCONLST))
	      then HASHFILECHCONLST_(to 128 collect NIL))
                                                       (* Generate the scratchlist only to write on SMALLEXPR files)
          (if (AND (TESTBITS VALUETYPE SMALLEXPR EXPR)
		   ~(READTABLEP (EVALV 'HASHFILERDTBL)))
	      then HASHFILERDTBL_(COPYREADTABLE 'ORIG))
          (PUTHASH FILE HASHFILE SYSHASHFILEARRAY)
          (WHENCLOSE FILE 'AFTER 'HASHAFTERCLOSE)
          (RETURN HASHFILE])

(POW2
  [LAMBDA (N)                                          (* rmk: " 4-SEP-78 21:18")
    (bind I_0 until N_(LRSH N 1)=0 do I_I+1 finally (RETURN I])

(PREPVALUE
  [LAMBDA NIL                                          (* rmk: "11-JAN-78 12:57")
                                                       (* Set CHARPOS to place on PAGE to put VALUE and fix up SLOT 
						       and free pointers)
    (DECLARE (USEDFREE PAGE SLOT REPLACE STRINGLENGTH))
    (PROG (END CHARPOS)
          (RETURN (if (AND REPLACE (STRINGLENGTH LEQ SLOT:VLength))
		      then                             (* We are replacing the value, and the new value is not 
						       longer than the old, so reuse the space)
			   (SLOT:VLength_STRINGLENGTH)
			   (IBOX SLOT:VCharPos)
		    elseif END_(IBOX STRINGLENGTH+PAGE:FirstFree) LEQ (CONSTANT MaxCharLocation)
		      then                             (* Normal case: putting VALUE into empty or deleted SLOT, or 
						       replacing with larger value)
			   (SLOT:VLength_STRINGLENGTH)
			   (SLOT:VCharPos_CHARPOS_(IBOX PAGE:FirstFree))
			   (PAGE:FirstFree_END)
			   CHARPOS])

(PRINTREGION
  [LAMBDA (P)                                          (* rmk: " 7-SEP-78 21:40")
                                                       (* Makes the start of the printing region PrintMargin pages above P)
    [SETFILEPTR HASHFILE:File (IBOX (CONSTANT CharsPerPage)*(HASHFILE:PrintPage#_P+(CONSTANT 
										      PrintMargin]
                                                       (* The PRIN1 makes the page dirty)
    (PRIN1 T HASHFILE:File])

(PUTHASHFILE
  [LAMBDA (KEY VALUE HASHFILE)                         (* rmk: "26-APR-79 09:52" posted: "13-DEC-77 09:24")
    (LOOKUPHASHFILE1 KEY VALUE (HFP HASHFILE T NIL T)
		     (if VALUE
			 then (CONSTANT (LOGOR INSERT REPLACE))
		       else (CONSTANT DELETE)))
    VALUE])

(PUTVALUE
  [LAMBDA (REPLACE)                                    (* rmk: "25-APR-79 16:47")
                                                       (* Returns NIL if unsuccessful; REPLACE means SLOT is being reused)
    (DECLARE (USEDFREE VAL HASHFILERDTBL SLOT REALPAGE# PAGE))
    (SELVALTYPEQ HASHFILE:HValueType
		 (NUMBER SLOT:Value_VAL T)
		 (STRING (PROG (STRINGLENGTH CHARPOS (INDEXPTR (IBOX)))
			       (PREPKEY VAL)           (* Computes STRINGLENGTH and the byte pointer INDEXPTR)
			       (if ~(CHARPOS_(PREPVALUE))
				   then (RETURN))      (* Fixes up pointers on PAGE and sets CHARPOS)
			       (COPYCHARSV PAGE CHARPOS)
                                                       (* Copy chars from INDEXPTR to CHARPOS for STRINGLENGTH)
			       (RETURN T)))
		 (SMALLEXPR (PROG (STRINGLENGTH CHARPOS)
			          (if (OR (STRINGLENGTH_(NCHARS VAL T HASHFILERDTBL)+ 1 gt
					    (CONSTANT MaxStringLength))
					  STRINGLENGTH=1)
				      then (ERROR 
					     "{in PUTHASHFILE/LOOKUPHASHFILE} bad value length: "
						  VAL T))
			          (if ~(CHARPOS_(PREPVALUE))
				      then (RETURN))
			          (PRINTCHARS PAGE CHARPOS VAL)
			          (RETURN T)))
		 (EXPR (SETFILEPTR HASHFILE:File -1)
		                                       (* Print VAL, storing the byte pointer as Value)
		       SLOT:Value_
		       (GETFILEPTR HASHFILE:File)
		                                       (* 2^24 is a VERY safe maximum)
		       (PRIN4 VAL HASHFILE:File HASHFILERDTBL)
		                                       (* Don't need linelength processing, nor closing separator if LISTP.)
		       (if (NLISTP VAL)
			   then (SPACES 1 HASHFILE:File))
		       T)
		 (2NUMBERS SLOT:Value_ (OR (FIXP (LISTP VAL):1)
					   (ERROR "Bad value for 2NUMBERS hashfile" VAL))
			   (replace I of (WORDOFFSET SLOT PAGE:#Slots)
			      with (OR (FIXP VAL::1)
				       (ERROR "Bad value for 2NUMBERS hashfile" VAL)))
			   T)
		 (SYMBOLTABLE (PROG (SYMBOL (OLD (IBOX SLOT:Value)))

          (* After we set up the new value, we might have to go out and smash the index pointer of the symbol formerly named by this slot)


				    (if VAL
					then (SYMBOL_(HLOCATE SLOT:Value_(OR (SMALLP VAL)
									     (IBOX VAL:FileAddress))
							      HASHFILE))
                                                       (* Assume VAL is a FILEHANDLE. Its important to store it in SLOT before
						       locating the symbol, lest the page disappear)
					     (SYMBOL:IndexPage#_REALPAGE#)
					     (SYMBOL:IndexWord#_(LOC SLOT))
					     (if ~(IEQP OLD 0)
						 then (replace IndexPointer
							 of (HLOCATE OLD HASHFILE) with 0))
				      else (ERROR "Nothing to be named")))
			      T)
		 (HELP 'PUTVALUE])

(REHASHFILE
  [LAMBDA (HASHFILE)                                   (* rmk: "29-OCT-78 08:22")
                                                       (* Rehash all the hash pages of HASHFILE.
						       Leaves all non-hash pages alone. Cleans up the world if there have been
						       alot of deletions.)
    (RESETLST (PROG (X)
		    (if X_(HFP HASHFILE T)
			then HASHFILE_X
		      elseif HASHFILE_(OPENHASHFILE HASHFILE 'BOTH)
			then (RESETSAVE NIL <'CLOSEF? HASHFILE:File>)
		      else (RETURN))
		    (REHASHPAGES (COLLECTPAGES HASHFILE)
				 HASHFILE)
		    (RETURN HASHFILE])

(REHASHPAGE
  [LAMBDA (DSLOT#)                                     (* rmk: "22-MAY-79 10:56")
                                                       (* Come here when the page referenced by DSLOT# is full and needs to be
						       rehashed)
    (DECLARE (USEDFREE REALPAGE#))
    (PROG (#SLOTS DIRPAGE)
          [#SLOTS_(if (FGREATERP #SLOTS_(SCANHASHFILE REALPAGE#)::1 0.0)
		      then (GET#SLOTS #SLOTS)
		    else (fetch #Slots of (HMAPIN REALPAGE# HASHFILE]
          (DIRPAGE_(GETDIRPAGE HASHFILE))
          (NEWDIRSLOTS DIRPAGE DSLOT# (fetch Depth of (WORDOFFSET DIRPAGE DSLOT#))
		       #SLOTS 2)                       (* Replace full page with 2 new ones)
          (REHASHPAGES REALPAGE# HASHFILE)             (* Rehash the full page)
          (PUTDIRPAGE DIRPAGE HASHFILE)                (* Now map the copied directory back to the file)
          (CLEARMAP HASHFILE:File REALPAGE#)
          (DELFILEPAGE HASHFILE REALPAGE#])

(REHASHPAGES
  [LAMBDA (PAGES HASHFILE)                             (* rmk: " 2-NOV-78 22:26")
                                                       (* Rehashes PAGES of HASHFILE)
    (RESETLST (RPTQ 4 (ADDMAPBUFFER T))                (* We need to lock one page, and it helps to keep the directory and the
						       2 new pages in core.)
                                                       (* Reduce valuetype to one that rehashes cheaper)
	      (if HASHFILE:HValueType=(CONSTANT SMALLEXPR)
		  then HASHFILE_(create HashFile reusing HASHFILE HValueType _(CONSTANT STRING))
		elseif HASHFILE:HValueType=(CONSTANT EXPR)
		  then HASHFILE_(create HashFile reusing HASHFILE HValueType _(CONSTANT NUMBER)))
	      (for P# PPTR (FILE _(fetch File of HASHFILE)) inside PAGES
		 do (RESETLST (RESETSAVE (LOCKMAP PPTR_(MAPPAGE P# FILE))
					 '(PROGN (UNLOCKMAP OLDVALUE)))
			      (inpage PPTR do (LOOKUPHASHFILE1 (GETINDEX SLOT PAGE)
							       (GETVALUE)
							       HASHFILE
							       (CONSTANT (LOGOR REHASH INSERT])

(SCANHASHFILE
  [LAMBDA (PAGES)                                      (* rmk: "22-MAY-79 18:06")
                                                       (* Scan PAGES of HASHFILE, looking at the number of slots occupied and 
						       how many characters they use. Return 
						       (#Slots . avg.chars.per.slot))
    (for PAGE# inside PAGES bind (#SLOTS _(IBOX 0))
				 (TOTAL _(IBOX 0))
				 (FILE _(fetch File of HASHFILE))
				 (STRINGTYPE _(TESTBITS HASHFILE:HValueType STRING/SMALLEXPR))
       do (inpage (MAPPAGE PAGE# FILE) do              (* look at filled slots, compute length of strings in use)
					  (add #SLOTS:I 1)
					  (add TOTAL:I (INDEXLENGTH PAGE SLOT)
					       (if STRINGTYPE
						   then SLOT:VLength
						 else 0)))
       finally (RETURN (CBOX #SLOTS (if #SLOTS:I gt 0
					then (FBOX (FQUOTIENT TOTAL:I #SLOTS:I))
				      else 0])

(VALUEBOX
  [LAMBDA (VTYPE)                                      (* rmk: "25-APR-79 12:07")
                                                       (* Creates the static boxes in which the hash values are returned.
						       Boxes for EXPR and SMALLEXPR files are used only for rehashing.)
    (SELVALTYPEQ VTYPE
		 ((NUMBER SYMBOLTABLE EXPR)
		   (IPLUS 10000))
		 ((STRING SMALLEXPR)
		   (CONCAT))
		 (2NUMBERS < (IPLUS 10000)
			   !
			   (IPLUS 10000)
			   >)
		 NIL])

(VALUETYPENUM
  [LAMBDA (VALTYPE)                                    (* rmk: "25-APR-79 11:42")
                                                       (* Coerces symbolic valuetype names to their codes)
    (SELECTQ VALTYPE
	     (NUMBER (CONSTANT NUMBER))
	     (STRING (CONSTANT STRING))
	     (SMALLEXPR (CONSTANT SMALLEXPR))
	     (EXPR (CONSTANT EXPR))
	     (SYMBOLTABLE (CONSTANT SYMBOLTABLE))
	     (2NUMBERS (CONSTANT 2NUMBERS))
	     (ERROR "Unrecognized hashfile value type" VALTYPE])
)
(DECLARE: EVAL@COMPILE DONTCOPY 

(RPAQQ HASHMACROS (BYTEPOINT COMPUTEHASHKEY DELETED? DELFILEPAGE EMPTY? FIND1STPRIME GETDIRPAGE 
			     GETINDEX GETSLOT HMAPIN INDEXLENGTH INUSE? KEYDIV HLOCATE LOWESTFREEPAGE 
			     USEDPAGE NEXTFREEPAGE MAKEDELETED MATCHINDEX PICKPAGE PREPKEY PRINTCHARS 
			     PUTDIRPAGE PUTINDEX SELVALTYPEQ SMASHSTRINGPOINTERV TESTBITS))

(PUTPROPS BYTEPOINT MACRO [(WORD CHAR BYTESIZE)
			   (LOC (ASSEMBLE NIL
				          (CQ WORD)
				          (CQ2 (VAG CHAR))
				          [E (STORIN (LIST 'IDIVI 2 ', (IQUOTIENT 44Q BYTESIZE]
				          (ADDI 1 , 0 (2))
				          [E (STORIN (LIST 'IMULI 3 ', (IMINUS (LLSH BYTESIZE 14Q]
				          (E (STORIN (LIST 'HRLI 1 ', (IPLUS 440000Q
									     (LLSH BYTESIZE 6))
							   (QUOTE (3])

(PUTPROPS COMPUTEHASHKEY MACRO [(INDEX)
	   (IBOX (LOC (ASSEMBLE NIL                    (* Computes hash of INDEX)
			        (CQ (PREPKEY INDEX))   (* PREPKEY macro gets bytepointer in 3, length in 4, and sets INDEXPTR 
						       and STRINGLENGTH; also checks for overflows)
			        (MOVEI 1 , 0)
			    LP  (ILDB 2 , 3)
			        [E (AND (NEQ CharMask 177Q)
					(STORIN (LIST 'ANDI 2 ', CharMask]
			        (ADDI 1 , 0 (2))
			        (IMUL 1 , = 240501202405Q)
			        (SOJG 4 , LP)
			        (MOVM 1 , 1)           (* Take abs value to avoid sign bit problems)
			    ])

(PUTPROPS DELETED? MACRO ((SLOT)
			  (EQ (OPENR (LOC SLOT))
			      -1)))

(PUTPROPS DELFILEPAGE MACRO ((HFILE PAGE#)
			     (JSYS 46 -1 (LOGOR (LLSH (fetch FileJfn of HFILE)
						      18)
						PAGE#))))

(PUTPROPS EMPTY? MACRO [(SLOT)
			(ZEROP (OPENR (LOC SLOT])

(PUTPROPS FIND1STPRIME MACRO [(N)
			      (LOC (ASSEMBLE NIL
					     (CQ (VAG N))
					     (IORI 1 , 1)
                                                       (* make 1 odd)
					     (SKIPA)
					 LP  (ADDI 1 , 2)
					     (SKIPA 2 , = 3)
					 LP2 (ADDI 2 , 2)
					     (MOVE 3 , 1)
					     (IDIVI 3 , 0 (2))
					     (JUMPE 4 , LP)
                                                       (* JUMP if 2 divides 1)
					     (CAIL 3 , 0 (2))
					     (JRST LP2)
                                                       (* 2 < SQRT (1))
					 ])

(PUTPROPS GETDIRPAGE MACRO [HFILE (LIST (QUOTE HMAPIN)
					0
					(OR (CAR HFILE)
					    (QUOTE HASHFILE])

(PUTPROPS GETINDEX MACRO [(SLOT PAGE)
			  (ASSEMBLE NIL

          (* For index strings. Returns a constant stringpointer pointing at string at POS on PAGE. Assumes that length of string is stored in the
	  first byte at POS)


				    [CQ (VAG (IPLUS (fetch CharPointer of SLOT)
						    (ITIMES 5 (LOC PAGE]
                                                       (* The absolute core byte address of the first byte of the string)
				    (MOVE 2 , 1)
				    (IDIVI 2 , 5)
				    (IMULI 3 , -70000Q)
				    (HRLI 2 , 440700Q (3))
				    (ILDB 2 , 2)
				    (ADDI 1 , 1)       (* Skip the length byte)
				    (LSH 2 , 25Q)
				    (IOR 2 , 1)        (* We now have the bits of a LISP string pointer in 2)
				    (CQ (CONSTANT (CONCAT)))
				    (MOVEM 2 , 0 (1])

(PUTPROPS GETSLOT MACRO [(PAGE SLOT#)
			 (ASSEMBLE NIL
			           (CQ PAGE)
			           (CQ2 (VAG SLOT#))
			           (E (STORIN (LIST (QUOTE ADDI)
						    1
						    (QUOTE ,)
						    #InitialWordsOnPage
						    (QUOTE (2])

(PUTPROPS HMAPIN MACRO ((PAGE# HFILE)
			(MAPPAGE PAGE# (fetch File of HFILE))))

(PUTPROPS INDEXLENGTH MACRO [(PAGE SLOT)
			     (LOC (ASSEMBLE NIL
					    (CQ (VAG (BYTEPOINT PAGE (fetch CharPointer of SLOT)
								BitsPerChar)))
					    (ILDB 1 , 1])

(PUTPROPS INUSE? MACRO ((SLOT)
			(ASSEMBLE NIL
			          (CQ SLOT)
			          (SKIPN 1 , 0 (1))
			          (JRST FALSE)
			          (AOSE 1)
			          (SKIPA 1 , KT)       (* True if SLOT not 0 or -1)
			      FALSE
			          (CQ NIL))))

(PUTPROPS KEYDIV MACRO [(N)
			(LOC (ASSEMBLE NIL
				       (CQ HASHKEY)
				       (CQ2 (VAG N))
				       (MOVE 3 , 0 (1))
				       (IDIVI 3 , 0 (2))
				       (MOVEM 3 , 0 (1))
				       (MOVE 1 , 4])

(PUTPROPS HLOCATE MACRO ((FILEADR HFILE)
			 (MAPWORD FILEADR (fetch File of HFILE))))

(PUTPROPS LOWESTFREEPAGE MACRO [(STARTPAGE JFN)
				(LOC (ASSEMBLE NIL
					       (CQ (VAG STARTPAGE))
                                                       (* Gets the free page above which all other pages are free.
						       Uses STARTPAGE as a known free page to start looking at)
					       (CQ2 (VAG JFN))
					       (HRLI 1 , 0 (2))
					   LP  (HRRZI 3 , 0 (1))
                                                       (* save page number in case it's the answer)
					       (JSYS 211Q)
                                                       (* The FFUFP JSYS)
					       (SKIPA)
                                                       (* Error, implies there are no higher used pages, so this page is the 
						       one to return)
					       (AOJA 1 , LP)
                                                       (* Success, implies the page now in 1 is in use, so add 1 and try 
						       again)
					   OUT (HRRZI 1 , 0 (3))
                                                       (* recover the answer)
					   ])

(PUTPROPS USEDPAGE MACRO ((PAGE JFN)
			  (ASSEMBLE NIL                (* Returns first used page above PAGE, NIL if PAGE is highest.)
				    (CQ (VAG PAGE))
				    (CQ2 (VAG JFN))
				    (HRLI 1 , 0 (2))   (* Move the JFN into the left-half)
				    (JSYS 211Q)        (* FFUFP JSYS)
				    (JUMPA NOTFOUND)   (* Skips if there exists a higher jused page)
				    (HRRZI 1 , 0 (1))
                                                       (* Clear the left half)
				    (FASTCALL MKN)
				    (SKIPA)
				NOTFOUND
				    (MOVE 1 , KNIL)
				OUT)))

(PUTPROPS NEXTFREEPAGE MACRO [(PAGE JFN)
			      (LOC (ASSEMBLE NIL       (* Returns the next free page above PAGE)
					     (CQ (VAG PAGE))
					     (ADDI 1 , 1)
                                                       (* Add one to start at the next page)
					     (CQ2 (VAG JFN))
					     (HRLI 1 , 0 (2))
					 LP  (JSYS 57Q)
                                                       (* RPACS JSYS. Page is free if B5=0)
					     (TLNE 2 , 10000Q)
                                                       (* Skip if free)
					     (AOJA 1 , LP)
					     (HRRZI 1 , 0 (1])

(PUTPROPS MAKEDELETED MACRO ((SLOT)
			     (SETWORDCONTENTS SLOT -1)))

(PUTPROPS MATCHINDEX MACRO (NIL (ASSEMBLE NIL          (* True if the index string associated with SLOT is same as INDEX)
				          [CQ (VAG (clisp (BYTEPOINT PAGE SLOT:CharPointer 
								     BitsPerChar]
				          (CQ2 INDEXPTR)
				          (MOVE 4 , 0 (2))
                                                       (* 4 contains bytepointer to index)
				          (CQ2 STRINGLENGTH)
				          (SUBI 2 , ASZ)
				          (ILDB 3 , 1)
				          (CAME 3 , 2)
				          (JRST FAIL)
				      LP  (ILDB 5 , 4)
				          (ILDB 3 , 1)
				          [E (AND (NEQ CharMask 177Q)
						  (STORIN (LIST 'ANDI 3 ', CharMask]
				          (CAME 3 , 5)
				          (JRST FAIL)
				          (SOJG 2 , LP)
				          (SKIPA 1 , KT)
				      FAIL(MOVE 1 , KNIL))))

(PUTPROPS PICKPAGE MACRO [NIL (fetch Page# of (WORDOFFSET (GETDIRPAGE)
							  (LOGAND HASHKEY (CONSTANT WordMask])

(PUTPROPS PREPKEY MACRO ((INDEX)
			 (ASSEMBLE NIL
			           (CQ (SELECTQ (NTYP INDEX)
						(30Q INDEX)
						[14Q (CDR (VAG (IPLUS 2 (LOC INDEX]
						(ERROR INDEX "not atom or string")))
			           (PUSHJ CP , UPATM)
			           (E (STORIN (LIST (QUOTE CAIG)
						    4
						    (QUOTE ,)
						    MaxStringLength)))
			           (CAIG 4 , 0)
			           (JRST ERROR)
			           (MOVEI 1 , ASZ (4))
			           (SETQ STRINGLENGTH)
			           (CQ INDEXPTR)
			           (MOVEM 3 , 0 (1))   (* Store bytepointer in number box INDEXPTR)
			           (JRST DONE)
			       ERROR
			           (CQ (ERROR INDEX "too long"))
			       DONE)))

(PUTPROPS PRINTCHARS MACRO ((PGE CHARPOS VAL)
			    (ASSEMBLE NIL              (* Prints VAL on PGE starting at CHARPOS.
						       I'd do this with RPLSTRING if it took a FLG and RDTBL arg as CHCON 
						       does)
				      (CQ (DCHCON VAL HASHFILECHCONLST T HASHFILERDTBL))
				      (CQ2 (VAG (BYTEPOINT PGE CHARPOS BitsPerChar)))
                                                       (* AC1 has non-null list of character codes for the PRIN2 pname of 
						       VALUE; AC2 is byte pointer to right place on page)
				  LP  (HRRZ 3 , 0 (1))
				      (SUBI 3 , ASZ)
				      (IDPB 3 , 2)
				      (CDR1)
				      (CAME 1 , KNIL)
				      (JRST LP)        (* finish up by printing a space)
				      (MOVEI 3 , 40Q)
				      (IDPB 3 , 2)
				      (MOVE 1 , KT))))

(PUTPROPS PUTDIRPAGE MACRO ((DIRPAGE HASHFILE)
			    (PROGN NIL)))

(PUTPROPS PUTINDEX MACRO [NIL (PROG [END (CHARPOS (IBOX (fetch FirstFree of PAGE]
                                                       (* Writes INDEX on PAGE and points SLOT at it;
						       returns NIL if unsuccessful. Smashes END and CHARPOS to avoid boxing)
				    (COND
				      ((NOT (IGREATERP (SETQ END (IBOX (IPLUS STRINGLENGTH CHARPOS 1))
							 )
						       (CONSTANT MaxCharLocation)))
					               (* The extra 1 is for the length count)
					(COPYCHARSI PAGE CHARPOS)
					               (* Copy chars from INDEXPTR to CHARPOS for STRINGLENGTH)
					(replace FirstFree of PAGE with END)
					(replace CharPointer of SLOT with CHARPOS)
					(RETURN T])

(PUTPROPS SELVALTYPEQ MACRO [ARGS
	    (CONS (QUOTE SELECTQ)
		  (CONS (CAR ARGS)
			(MAPLIST (CDR ARGS)
				 (FUNCTION (LAMBDA (TAIL)
				     (COND
				       ((CDR TAIL)
					 (CONS (COND
						 ([LITATOM (CAR (SETQ TAIL (CAR TAIL]
						   (EVAL (CAR TAIL)))
						 ((LISTP (CAR TAIL))
						   (MAPCAR (CAR TAIL)
							   (FUNCTION EVAL)))
						 (T (SHOULDNT)))
					       (CDR TAIL)))
				       (T (CAR TAIL])

(PUTPROPS SMASHSTRINGPOINTERV MACRO [(LEN POS PAGE SCRATCHPOINTER)
	   (ASSEMBLE NIL                               (* Smash STRINGPOINTER to point at string at POS on PAGE of length LEN)
		     (CQ SCRATCHPOINTER)
		     [CQ2 (VAG (LOGOR (LLSH LEN 25Q)
				      (IPLUS POS (ITIMES (CONSTANT CharsPerWord)
							 (LOC PAGE]
		     (MOVEM 2 , 0 (1])

(PUTPROPS TESTBITS MACRO [(N . BITS)
			  (NOT (ZEROP (LOGAND N (CONSTANT (LOGOR . BITS])


(RPAQQ HASHMACROS (BYTEPOINT COMPUTEHASHKEY DELETED? DELFILEPAGE EMPTY? FIND1STPRIME GETDIRPAGE 
			     GETINDEX GETSLOT HMAPIN INDEXLENGTH INUSE? KEYDIV HLOCATE LOWESTFREEPAGE 
			     USEDPAGE NEXTFREEPAGE MAKEDELETED MATCHINDEX PICKPAGE PREPKEY PRINTCHARS 
			     PUTDIRPAGE PUTINDEX SELVALTYPEQ SMASHSTRINGPOINTERV TESTBITS))
(SETTEMPLATE (QUOTE BYTEPOINT)
	     (QUOTE MACRO))
(SETTEMPLATE (QUOTE COMPUTEHASHKEY)
	     (QUOTE MACRO))
(SETTEMPLATE (QUOTE DELETED?)
	     (QUOTE MACRO))
(SETTEMPLATE (QUOTE DELFILEPAGE)
	     (QUOTE MACRO))
(SETTEMPLATE (QUOTE EMPTY?)
	     (QUOTE MACRO))
(SETTEMPLATE (QUOTE FIND1STPRIME)
	     (QUOTE MACRO))
(SETTEMPLATE (QUOTE GETDIRPAGE)
	     (QUOTE MACRO))
(SETTEMPLATE (QUOTE GETINDEX)
	     (QUOTE MACRO))
(SETTEMPLATE (QUOTE GETSLOT)
	     (QUOTE MACRO))
(SETTEMPLATE (QUOTE HMAPIN)
	     (QUOTE MACRO))
(SETTEMPLATE (QUOTE INDEXLENGTH)
	     (QUOTE MACRO))
(SETTEMPLATE (QUOTE INUSE?)
	     (QUOTE MACRO))
(SETTEMPLATE (QUOTE KEYDIV)
	     (QUOTE MACRO))
(SETTEMPLATE (QUOTE HLOCATE)
	     (QUOTE MACRO))
(SETTEMPLATE (QUOTE LOWESTFREEPAGE)
	     (QUOTE MACRO))
(SETTEMPLATE (QUOTE USEDPAGE)
	     (QUOTE MACRO))
(SETTEMPLATE (QUOTE NEXTFREEPAGE)
	     (QUOTE MACRO))
(SETTEMPLATE (QUOTE MAKEDELETED)
	     (QUOTE MACRO))
(SETTEMPLATE (QUOTE MATCHINDEX)
	     (QUOTE MACRO))
(SETTEMPLATE (QUOTE PICKPAGE)
	     (QUOTE MACRO))
(SETTEMPLATE (QUOTE PREPKEY)
	     (QUOTE MACRO))
(SETTEMPLATE (QUOTE PRINTCHARS)
	     (QUOTE MACRO))
(SETTEMPLATE (QUOTE PUTDIRPAGE)
	     (QUOTE MACRO))
(SETTEMPLATE (QUOTE PUTINDEX)
	     (QUOTE MACRO))
(SETTEMPLATE (QUOTE SELVALTYPEQ)
	     (QUOTE MACRO))
(SETTEMPLATE (QUOTE SMASHSTRINGPOINTERV)
	     (QUOTE MACRO))
(SETTEMPLATE (QUOTE TESTBITS)
	     (QUOTE MACRO))

[DECLARE: EVAL@COMPILE 

(ARRAYBLOCK HashFile ((HValueType BITS 18)
		      (FileJfn BITS 18)
		      (PrintPage# BITS 18)
		      (HWordsPerSlot BITS 18)
		      File Tag Access ValueBox)        (* PrintPage# keeps a page# in the current block of expr printing)
		     )

(BLOCKRECORD Slot ((CharPointer BITS 12)
		   (Value BITS 24))
		  (BLOCKRECORD Slot ((NIL BITS 12)
				(NIL BITS 5)
				(VLength BITS 7)
				(VCharPos BITS 12)))   (* An encoding for character values)
		  )

(BLOCKRECORD Page ((#Slots BITS 18)
		   (FirstFree BITS 18))                (* Actually, Page is a record of WordsPerPage words, the first 2 of 
						       which are given, then #Slots slots, then characters)
                                                       (* FirstFree is a relative (to the beginning of the page) pointer to 
						       the first unused character in the extension table)
		  )

(BLOCKRECORD DirSlot ((NIL BITS 14)
		      (Depth BITS 4)
		      (Page# BITS 18))
		     (BLOCKRECORD DirSlot ((NIL BITS 14)
				   (ReHashInfo BITS 22))))

(BLOCKRECORD DirPage ((ValueType BITS 9)
		      (NIL BITS 27)))

(ARRAYBLOCK HASHFILESPLST (INIT FN HASHFILE SLOT LASTSLOT PAGE PAGELST)
                                                       (* For hashfile spelling correction.
						       FN = HASHFILESPLST1, the others save state info of where we are in 
						       mapping over the file)
			  )

(BLOCKRECORD SYMBOL ((NIL BITS 12)
		     (IndexPage# BITS 15)
		     (IndexWord# BITS 9))
		    (BLOCKRECORD SYMBOL ((NIL BITS 12)
				  (IndexPointer BITS 24))))

(BLOCKRECORD FILEHANDLE ((NIL BITS 12)
			 (FileAddress BITS 24)))
]


(RPAQQ VALUETYPES ((NUMBER 1)
		   (STRING 2)
		   (SMALLEXPR 3)
		   (EXPR 4)
		   (SYMBOLTABLE 5)
		   (2NUMBERS 8 (* So that bit 2 is off and the LOGAND is accurate))
		   (STRING/SMALLEXPR (LOGAND STRING SMALLEXPR))))

(RPAQ NUMBER 1)

(RPAQ STRING 2)

(RPAQ SMALLEXPR 3)

(RPAQ EXPR 4)

(RPAQ SYMBOLTABLE 5)

(RPAQ 2NUMBERS 8 (* So that bit 2 is off and the LOGAND is accurate))

(RPAQ STRING/SMALLEXPR (LOGAND STRING SMALLEXPR))


(RPAQQ CALLTYPES ((INSERT 1)
		  (RETRIEVE 4)
		  (DELETE 8)
		  (REPLACE 16)
		  (REHASH 32)))

(RPAQ INSERT 1)

(RPAQ RETRIEVE 4)

(RPAQ DELETE 8)

(RPAQ REPLACE 16)

(RPAQ REHASH 32)


(RPAQ WRITETYPES (LOGOR INSERT REPLACE DELETE))

(RPAQ WordsPerPage 512)

(RPAQ WordBits 9)

(RPAQ WordMask (WordsPerPage-1))

(RPAQ BitsPerWord 36)

(RPAQ BitsPerChar 7)

(RPAQ CharMask (2^BitsPerChar-1))

(RPAQ CharsPerWord BitsPerWord/BitsPerChar)

(RPAQ CharsPerPage CharsPerWord*WordsPerPage)

(RPAQ MaxCharLocation CharsPerPage-1)

(RPAQ MaxStringLength 127)

(RPAQ #InitialWordsOnPage 1)

(RPAQ DefaultKeySize 5)

(RPAQ DefaultInitial#Pages 2)

(RPAQ PrintMargin 128)

(LOAD? (OR (FINDFILE (QUOTE NOBOX.COM)
		     T LISPUSERSDIRECTORIES)
	   (QUOTE NOBOX.COM))
       (QUOTE SYSLOAD))

(RESETSAVE DWIMIFYCOMPFLG T)
(CLISPDEC (QUOTE FAST))
(SETQ FONTFNS HASHMACROS)

[I.S.OPR (QUOTE inpage)
	 NIL
	 (QUOTE (BIND (PAGE_ BODY)
		      SLOT LASTSLOT FIRST (SETQ SLOT (GETSLOT PAGE 0))
		      (SETQ LASTSLOT (GETSLOT PAGE (fetch #Slots of PAGE)))
		      WHEN
		      (INUSE? SLOT)
		      REPEATWHILE
		      (NEQ (SETQ SLOT (WORDOFFSET SLOT 1))
			   LASTSLOT]


(ADDTOVAR PRETTYEQUIVLST (SELVALTYPEQ . SELECTQ))

(ADDTOVAR DWIMEQUIVLST (SELVALTYPEQ . SELECTQ))
)

(RPAQQ HASHBLOCKS ((HASHFILEBLOCK (ENTRIES COPYHASHFILE CREATEHASHFILE DELPAGE GETHASHFILE GETPAGE 
					   GETPNAME HASHAFTERCLOSE HASHFILENAME HASHFILEP 
					   HASHFILEPROP HASHFILESPLST HASHFILESPLST1 HASHSTATUS 
					   LOOKUPHASHFILE MAPHASHFILE OPENHASHFILE PUTHASHFILE 
					   REHASHFILE)
				  (LOCALFREEVARS HASHFILE INDEXPTR PAGE REALPAGE# REPLACE SLOT 
						 STRINGLENGTH VAL)
				  (GLOBALVARS HASHFILECHCONLST HASHFILERDTBL HASHFILESPLST 
					      SYSHASHFILE SYSHASHFILEARRAY)
				  COLLECTPAGES COPYCHARSI COPYCHARSV COPYHASHFILE COPYHASHPAGES 
				  CREATEHASHFILE DELPAGE GET#SLOTS GETFILEPAGE GETHASHFILE GETPAGE 
				  GETPNAME GETVALUE HASHAFTERCLOSE HASHFILENAME HASHFILEP 
				  HASHFILEPROP HASHFILESPLST HASHFILESPLST1 HASHSTATUS HFP 
				  INITHASHPAGE LOOKUPHASHFILE LOOKUPHASHFILE1 MAPHASHFILE NEWDIRSLOTS 
				  OPENHASHFILE POW2 PREPVALUE PRINTREGION PUTHASHFILE PUTVALUE 
				  REHASHFILE REHASHPAGE REHASHPAGES SCANHASHFILE VALUEBOX 
				  VALUETYPENUM)))
[DECLARE: DONTEVAL@LOAD DOEVAL@COMPILE DONTCOPY
(BLOCK: HASHFILEBLOCK
	(ENTRIES COPYHASHFILE CREATEHASHFILE DELPAGE GETHASHFILE GETPAGE GETPNAME HASHAFTERCLOSE 
		 HASHFILENAME HASHFILEP HASHFILEPROP HASHFILESPLST HASHFILESPLST1 HASHSTATUS 
		 LOOKUPHASHFILE MAPHASHFILE OPENHASHFILE PUTHASHFILE REHASHFILE)
	(LOCALFREEVARS HASHFILE INDEXPTR PAGE REALPAGE# REPLACE SLOT STRINGLENGTH VAL)
	(GLOBALVARS HASHFILECHCONLST HASHFILERDTBL HASHFILESPLST SYSHASHFILE SYSHASHFILEARRAY)
	COLLECTPAGES COPYCHARSI COPYCHARSV COPYHASHFILE COPYHASHPAGES CREATEHASHFILE DELPAGE 
	GET#SLOTS GETFILEPAGE GETHASHFILE GETPAGE GETPNAME GETVALUE HASHAFTERCLOSE HASHFILENAME 
	HASHFILEP HASHFILEPROP HASHFILESPLST HASHFILESPLST1 HASHSTATUS HFP INITHASHPAGE 
	LOOKUPHASHFILE LOOKUPHASHFILE1 MAPHASHFILE NEWDIRSLOTS OPENHASHFILE POW2 PREPVALUE 
	PRINTREGION PUTHASHFILE PUTVALUE REHASHFILE REHASHPAGE REHASHPAGES SCANHASHFILE VALUEBOX 
	VALUETYPENUM)
]

(RPAQ SYSHASHFILE NIL)

(RPAQ HASHFILESPLST NIL)

(RPAQ SYSHASHFILEARRAY (CONS (HARRAY 10)))

(ADDTOVAR HASHFILECHCONLST )
(DECLARE: DONTCOPY
  (FILEMAP (NIL (1823 38936 (CLOSEHASHFILE 1835 . 2008) (COLLECTPAGES 2012 . 2411) (COPYCHARSI 2415 . 
3403) (COPYCHARSV 3407 . 4042) (COPYHASHFILE 4046 . 6703) (COPYHASHPAGES 6707 . 7935) (CREATEHASHFILE 
7939 . 10413) (DELPAGE 10417 . 10848) (GET#SLOTS 10852 . 11854) (GETFILEPAGE 11858 . 12696) (
GETHASHFILE 12700 . 12921) (GETPAGE 12925 . 13284) (GETPNAME 13288 . 14090) (GETVALUE 14094 . 15008) (
HASHAFTERCLOSE 15012 . 15639) (HASHFILENAME 15643 . 15805) (HASHFILEP 15809 . 16029) (HASHFILEPROP 
16033 . 17104) (HASHFILESPLST 17108 . 17482) (HASHFILESPLST1 17486 . 19243) (HASHSTATUS 19247 . 19789)
 (HFP 19793 . 20702) (INITHASHPAGE 20706 . 21274) (LOOKUPHASHFILE 21278 . 21803) (LOOKUPHASHFILE1 
21807 . 25053) (MAPHASHFILE 25057 . 25807) (NEWDIRSLOTS 25811 . 26902) (OPENHASHFILE 26906 . 29067) (
POW2 29071 . 29248) (PREPVALUE 29252 . 30288) (PRINTREGION 30292 . 30774) (PUTHASHFILE 30778 . 31092) 
(PUTVALUE 31096 . 34039) (REHASHFILE 34043 . 34701) (REHASHPAGE 34705 . 35756) (REHASHPAGES 35760 . 
36904) (SCANHASHFILE 36908 . 37898) (VALUEBOX 37902 . 38412) (VALUETYPENUM 38416 . 38933)))))
STOP
