HASH.MOD 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. IMPLEMENTATION MODULE Hash;
  2. (*
  3. * REPERTOIRE
  4. * Release 1.6
  5. * By Charles Bradford and Cole Brecheen
  6. * (c) Copyright 1985-1992 PMI
  7. * Green Bay, Wisconsin
  8. * All rights reserved
  9. * (414) 468-6040
  10. *
  11. * $Header: D:/logfiles/mods/hash.mov 1.5 10 Mar 1991 15:28:22 coleb $
  12. *
  13. *
  14. * Written and contributed by Jonathan March, of San Francisco.
  15. *
  16. *)
  17. (* This module must be compiled with all runtime arithmetic checking
  18. turned off. The Compute procedure depends on an overflow. *)
  19. (*# check(overflow => off) *)
  20. (* This turns off overflow checking for the JPI compiler. *)
  21. (*/NOCHECK:O*)
  22. (* And this does it for Stony Brook. *)
  23. IMPORT ErrorManager;
  24. IMPORT LowLevel;
  25. IMPORT SYSTEM;
  26. IMPORT VStorage;
  27. VAR
  28. Initialized : BOOLEAN;
  29. PROCEDURE Init();
  30. BEGIN
  31. IF Initialized THEN
  32. RETURN;
  33. ELSE
  34. Initialized := TRUE;
  35. END;
  36. ErrorManager.Init();
  37. LowLevel.Init();
  38. VStorage.Init();
  39. END Init;
  40. CONST
  41. MaxTable = 999;
  42. (* Note: this is just an error-checking limit. The table is allocated *)
  43. (* dynamically, so MaxTable is just an upper limit to that allocation. *)
  44. (* It can be changed with no side effects if desired. *)
  45. InitCodeValue = 25967; (* arbitrary. *)
  46. TYPE HashTable = POINTER TO HashHeader;
  47. TYPE
  48. NodePtr = POINTER TO Node;
  49. Node =
  50. RECORD
  51. next : NodePtr;
  52. (* next node in this bin in hash table *)
  53. hash2, hash3 : CARDINAL;
  54. (* stored in lieu of actual key *)
  55. hData : ARRAY [0..1] OF SYSTEM.BYTE;
  56. (* Dummy. Data is actually longer... *)
  57. END; (* record *)
  58. BinHeader =
  59. RECORD
  60. first : NodePtr;
  61. binCount : CARDINAL; (* diagnostic use only. *)
  62. END; (* record *)
  63. HashArray = ARRAY [0..MaxTable-1] OF BinHeader;
  64. HashHeader =
  65. RECORD
  66. initCode : CARDINAL; (* will be InitCodeValue if initialized. *)
  67. binCount : CARDINAL; (* n of bins in hash table *)
  68. tableCount : CARDINAL; (* n entries currently in table. *)
  69. dataSize : CARDINAL; (* n bytes of user data per entry *)
  70. nodeSize : CARDINAL; (* n bytes allocated per entry *)
  71. tablePtr : POINTER TO HashArray; (* array of bin headers *)
  72. currentBinNum: CARDINAL; (* index of last bin accessed *)
  73. currentNode : NodePtr; (* last node accessed. NIL if none. *)
  74. parentNode : NodePtr; (* parent node of currentNode. NIL if top. *)
  75. END; (* record *)
  76. PROCEDURE Define
  77. ( VAR table : HashTable; (* out *)
  78. numberBins : CARDINAL; (* in *)
  79. dataBytes : CARDINAL (* in - bytes per data *)
  80. );
  81. (* Create and initialize a hash table. Number of bins would typically be
  82. about the same as the number of expected entries; more for speed, less
  83. for space saving. The number of bins will be increased by 1 if even.
  84. Note that the size of all user data in this table will be required to
  85. be "dataBytes" *)
  86. VAR
  87. i : CARDINAL;
  88. BEGIN (* procedure HashDefine *)
  89. IF (numberBins<2) OR (numberBins>MaxTable) THEN
  90. ErrorManager.CallHalt(
  91. "Programmer error initializing hash table: bad table size.");
  92. END; (* if numberBins *)
  93. VStorage.DosAlloc(table, SYSTEM.TSIZE(HashHeader) );
  94. WITH table^ DO
  95. binCount := CARDINAL( BITSET(numberBins) + BITSET(1)); (* force odd *)
  96. dataSize := dataBytes; (* bytes of user data per element *)
  97. nodeSize := SYSTEM.TSIZE( Node) - 2 + dataSize;
  98. (* byte allocated per element. *)
  99. tableCount:= 0;
  100. currentBinNum := 0;
  101. currentNode := NIL;
  102. parentNode := NIL;
  103. VStorage.DosAlloc( tablePtr, SYSTEM.TSIZE( BinHeader) * binCount );
  104. FOR i := 0 TO binCount-1 DO
  105. WITH tablePtr^[i] DO
  106. first := NIL;
  107. binCount := 0;
  108. END; (* with tablePtr *)
  109. END; (* for i *)
  110. initCode := InitCodeValue;
  111. END; (* with table *)
  112. END Define; (* procedure *)
  113. PROCEDURE Dispose( VAR table : HashTable); (* out *)
  114. (* releases all memory associated with the table. Table becomes NIL. *)
  115. (* Does nothing if table was not initialized. *)
  116. VAR
  117. pNode, qNode : NodePtr;
  118. count, iNode : CARDINAL;
  119. BEGIN (* procedure HashDispose *)
  120. count := 0;
  121. IF table # NIL THEN
  122. WITH table^ DO
  123. IF initCode = InitCodeValue THEN
  124. FOR iNode := 0 TO binCount-1 DO
  125. WITH tablePtr^[iNode] DO
  126. pNode := first;
  127. WHILE pNode # NIL DO
  128. INC( count);
  129. qNode := pNode^.next;
  130. VStorage.DosDealloc( pNode, nodeSize);
  131. pNode := qNode;
  132. END; (* while pNode *)
  133. END; (* with tablePtr *)
  134. END; (* for iNode *)
  135. VStorage.DosDealloc( tablePtr, SYSTEM.TSIZE( BinHeader)* binCount);
  136. IF count # tableCount THEN
  137. ErrorManager.CallHalt( "Hash table memory corruption detected.");
  138. END; (* if count *)
  139. END; (* if initCode *)
  140. initCode := 0; (* in case memory location is re-used. *)
  141. END; (* with table *)
  142. VStorage.DosDealloc( table, SYSTEM.TSIZE(HashHeader) );
  143. END; (* if table *)
  144. END Dispose; (* procedure *)
  145. PROCEDURE InitCheck(table : HashTable);
  146. (* Internal only *)
  147. BEGIN (* procedure InitCheck *)
  148. IF (table = NIL) OR (table^.initCode # InitCodeValue) THEN
  149. ErrorManager.CallHalt( "Programmer error: uninitialized hash table.");
  150. END; (* if table *)
  151. END InitCheck; (* procedure *)
  152. (* ALL THE FOLLOWING PROCEDURES WILL HALT THE PROGRAM IF CALLED WITH AN *)
  153. (* UNINITIALIZED TABLE. Initialization is the programmer's responsibility. *)
  154. (*$O-*)
  155. PROCEDURE Compute( key : ARRAY OF SYSTEM.BYTE;
  156. VAR hash1, hash2, hash3 : CARDINAL);
  157. (* return 3 independent hashed values of the key. *)
  158. VAR
  159. i, j, in1, in2 : CARDINAL;
  160. temp1, temp2, temp3 : CARDINAL;
  161. BEGIN (* procedure Compute *)
  162. (*$R-*)(*$T-*)
  163. temp1 := 5555H;
  164. temp2 := 9753H;
  165. temp3 := 0E069H;
  166. j := HIGH(key);
  167. FOR i := 0 TO HIGH(key) DO
  168. (* Each byte of the key is used twice, symmetrically at opposite ends *)
  169. (* of the hashing. Thus, for example, keys differing only in the last *)
  170. (* byte will end up hashed completely differently. *)
  171. in1 := ORD(key[i]);
  172. in2 := ORD(key[j]);
  173. DEC(j);
  174. temp1:= CARDINAL( BITSET(temp1*2) / BITSET(in1*8) / BITSET(temp2) ) +
  175. temp3 DIV 128 - in2*128;
  176. temp2:= CARDINAL( BITSET(temp2*4) / BITSET(in1*512 + in2) ) -
  177. temp1 DIV 64 + temp3;
  178. temp3:= CARDINAL( BITSET(temp3 DIV 4 - in1) / BITSET(in2*256) ) +
  179. temp2 * 64 + temp1;
  180. END; (* for i *)
  181. hash1 := temp1;
  182. hash2 := temp2;
  183. hash3 := temp3;
  184. (*$R=*)(*$T=*)
  185. END Compute; (* procedure *)
  186. PROCEDURE KeyFind
  187. ( table : HashTable; (* in *)
  188. key : ARRAY OF SYSTEM.BYTE (* in - key to look for *)
  189. ) : BOOLEAN; (* out - key found? *)
  190. (* See if an entry with this key already exists in this hash table. *)
  191. VAR
  192. h1, h2, h3 : CARDINAL;
  193. BEGIN (* procedure KeyFind *)
  194. InitCheck(table);
  195. WITH table^ DO
  196. Compute( key, h1, h2, h3);
  197. currentBinNum := h1 MOD binCount;
  198. currentNode := tablePtr^[currentBinNum].first;
  199. parentNode := NIL;
  200. WHILE currentNode # NIL DO
  201. WITH currentNode^ DO
  202. IF (h2=hash2) AND (h3=hash3) THEN
  203. RETURN TRUE;
  204. END; (* if h2 *)
  205. END; (* with currentNode *)
  206. (* no match yet, try the next node in linked list, if any: *)
  207. parentNode := currentNode;
  208. currentNode := currentNode^.next;
  209. END; (* while currentNode *)
  210. (* no match in the list. *)
  211. RETURN FALSE;
  212. END; (* with table *)
  213. END KeyFind; (* procedure *)
  214. PROCEDURE Insert
  215. ( table : HashTable; (* in/(out) *)
  216. key : ARRAY OF SYSTEM.BYTE; (* in - must be unique *)
  217. data : ARRAY OF SYSTEM.BYTE (* in - always same size.*)
  218. );
  219. (* Halts program if attempt is made to insert a duplicate key.
  220. This is a partial safeguard against (extremely unlikely) false key matching.
  221. First call KeyFind if you want to check for a duplicate before
  222. inserting. *)
  223. (* Inefficient, because computes the hash twice. ***** *)
  224. VAR
  225. h1 : CARDINAL;
  226. nodeBytes : CARDINAL;
  227. BEGIN (* procedure HashInsert *)
  228. IF KeyFind( table, key) THEN
  229. ErrorManager.CallHalt(
  230. "Programmer error or hash algorithm failure. Duplicate hash key.");
  231. END; (* if KeyFind *)
  232. WITH table^ DO
  233. IF dataSize # (HIGH(data)+1) THEN
  234. ErrorManager.CallHalt(
  235. "Programmer error calling HashInsert: wrong data size.");
  236. END; (* if dataSize *)
  237. parentNode := NIL;
  238. (* allocate amount actually needed for node. *)
  239. VStorage.DosAlloc( currentNode, nodeSize);
  240. WITH currentNode^ DO
  241. LowLevel.Move( SYSTEM.ADR(data), SYSTEM.ADR(hData), dataSize);
  242. Compute( key, h1, hash2, hash3);
  243. END; (* with currentNode *)
  244. currentBinNum := h1 MOD binCount; (* redundant, because of KeyFind. *)
  245. WITH tablePtr^[currentBinNum] DO
  246. currentNode^.next := first;
  247. first := currentNode;
  248. INC(binCount);
  249. END; (* with tablePtr *)
  250. INC( tableCount);
  251. END; (* with table *)
  252. END Insert; (* procedure *)
  253. PROCEDURE Delete( table : HashTable);
  254. (* Deletes hash table entry associated with the last Insert or KeyFind. *)
  255. (* Has no effect if that element was never found or was already deleted. *)
  256. VAR
  257. nextNode : NodePtr;
  258. BEGIN (* procedure HashDelete *)
  259. InitCheck(table);
  260. WITH table^ DO
  261. IF currentNode # NIL THEN
  262. DEC( tableCount);
  263. nextNode := currentNode^.next;
  264. VStorage.DosDealloc( currentNode, nodeSize);
  265. WITH tablePtr^[ currentBinNum] DO
  266. DEC( binCount);
  267. IF parentNode = NIL THEN
  268. first := nextNode;
  269. ELSE
  270. parentNode^.next := nextNode;
  271. END; (* if parentNode *)
  272. END; (* with tablePtr *)
  273. END; (* if currentNode *)
  274. END; (* with table *)
  275. END Delete; (* procedure *)
  276. PROCEDURE MoveData( table : HashTable;
  277. pIn, pOut : SYSTEM.ADDRESS;
  278. dataHigh : CARDINAL);
  279. (* used in HashGetData and HashChangeData. *)
  280. BEGIN (* procedure DataCheck *)
  281. WITH table^ DO
  282. IF (currentNode = NIL) OR (dataSize # dataHigh+1) THEN
  283. ErrorManager.CallHalt(
  284. "Programmer error. Attempt to access hash element.");
  285. END; (* if currentNode *)
  286. LowLevel.Move( pIn, pOut, dataSize);
  287. END; (* with table *)
  288. END MoveData; (* procedure *)
  289. PROCEDURE GetData
  290. ( table : HashTable; (* in *)
  291. VAR data : ARRAY OF SYSTEM.BYTE (* out - size must match. *)
  292. );
  293. (* Gets the data associated with the last Insert or KeyFind. *)
  294. (* Fails if no such element or size does not match. *)
  295. BEGIN (* procedure HashGetData *)
  296. InitCheck( table);
  297. MoveData(table, SYSTEM.ADR(table^.currentNode^.hData),
  298. SYSTEM.ADR(data), HIGH(data) );
  299. END GetData; (* procedure *)
  300. PROCEDURE ChangeData
  301. ( table : HashTable; (* in *)
  302. data : ARRAY OF SYSTEM.BYTE (* in - size must match. *)
  303. );
  304. (* Changes the data associated with the last Insert or KeyFind. *)
  305. (* Fails if no such element or size does not match. *)
  306. BEGIN (* procedure HashChangeData *)
  307. InitCheck( table);
  308. MoveData(table, SYSTEM.ADR(data),
  309. SYSTEM.ADR(table^.currentNode^.hData), HIGH(data) );
  310. END ChangeData; (* procedure *)
  311. PROCEDURE Size( table : HashTable) : CARDINAL;
  312. (* returns the number of data entries in the hash table. *)
  313. BEGIN (* procedure HashSize *)
  314. IF (table=NIL) OR (table^.initCode # InitCodeValue) THEN
  315. RETURN 0;
  316. END; (* if table *)
  317. RETURN table^.tableCount;
  318. END Size; (* procedure *)
  319. BEGIN
  320. Initialized := FALSE;
  321. Init();
  322. END Hash. (* implementation module *)