/[projects]/dao/DelphiScanner/Components/tpsystools_4.04/source/StHASH.pas
ViewVC logotype

Contents of /dao/DelphiScanner/Components/tpsystools_4.04/source/StHASH.pas

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2671 - (show annotations) (download)
Tue Aug 25 18:15:15 2015 UTC (8 years, 9 months ago) by torben
File size: 25905 byte(s)
Added tpsystools component
1 // Upgraded to Delphi 2009: Sebastian Zierer
2
3 (* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * The Original Code is TurboPower SysTools
17 *
18 * The Initial Developer of the Original Code is
19 * TurboPower Software
20 *
21 * Portions created by the Initial Developer are Copyright (C) 1996-2002
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * ***** END LICENSE BLOCK ***** *)
27
28 {*********************************************************}
29 {* SysTools: StHASH.PAS 4.04 *}
30 {*********************************************************}
31 {* SysTools: Hash table class *}
32 {*********************************************************}
33
34 {$I StDefine.inc}
35
36 {Notes:
37 - Generally the same as STDICT.PAS, but the hash table is
38 keyed on elements of arbitrary type rather than just strings.
39
40 - Also manages an LRU counter and updates each node's LRU when
41 it is added or accessed. If the maximum allowed number of nodes
42 in the table is exceeded, the least recently used node is
43 automatically removed from the table. By default, MaxLongInt
44 nodes can be in the table so the automatic removal logic does
45 not come into play. When a node is automatically removed, the
46 NodeRemoved virtual method is called to notify the program
47 that the node is being removed.
48 }
49
50 unit StHASH;
51
52 interface
53
54 uses
55 SysUtils,
56 Classes,
57 {$IFDEF ThreadSafe}
58 Windows,
59 {$ENDIF}
60 StConst,
61 StBase;
62
63 type
64 TStHashNode = class(TStNode)
65 {.Z+}
66 protected
67 hnNext : TStHashNode; {Next node in hash list}
68 hnValue: Pointer; {Pointer to value of element}
69 hnValSize : Cardinal; {Size of hnValue memory block}
70 FLRU : LongInt; {LRU counter of this node}
71
72 function GetValue : Pointer;
73
74 {.Z-}
75 public
76 constructor CreateNode(const AValue; AValSize : Cardinal; AData : Pointer);
77 virtual;
78 {-Initialize node}
79 destructor Destroy; override;
80 {-Free name string and destroy node}
81
82 property Value : Pointer
83 read GetValue;
84 property LRU : LongInt
85 read FLRU
86 write FLRU;
87 end;
88
89 {.Z+}
90 THashArray = array[0..(MaxInt div SizeOf(TStHashNode))-1] of TStHashNode;
91 PHashArray = ^THashArray;
92 {.Z-}
93
94 THashFunc = function (const V; Size : Integer) : Integer;
95
96 TStHashTable = class(TStContainer)
97 {.Z+}
98 protected
99 {property instance variables}
100 FValSize : Cardinal; {Size of each element in table}
101 FHashSize : Integer; {Bins in hash array}
102 FEqual : TUntypedCompareFunc; {Element compare function}
103 FHash : THashFunc; {Hash function}
104 FMaxNodes : LongInt; {Max nodes allowed in table}
105
106 {private instance variables}
107 htHeads : PHashArray; {Pointer to head of node lists}
108 htTails : PHashArray; {Pointer to tail of node lists}
109 htLRU : LongInt; {LRU counter}
110 htIgnoreDups : Boolean; {Ignore duplicates during Join?}
111
112 {protected undocumented methods}
113 procedure htInsertNode(H : Integer; This : TStHashNode);
114 procedure htIterate(Action : TIterateFunc; OtherData : Pointer;
115 var H : Integer; var Prev, This : TStHashNode);
116 procedure htSetEqual(E : TUntypedCompareFunc);
117 procedure htSetHash(H : THashFunc);
118 procedure htSetHashSize(Size : Integer);
119 procedure htSetMaxNodes(Nodes : LongInt);
120 procedure htMoveToFront(H : Integer; Prev, This : TStHashNode);
121 procedure htFindNode(const V; var H : Integer;
122 var Prev, This : TStHashNode);
123 procedure htUpdateLRU(This : TStHashNode);
124 procedure htDeleteOldestNode;
125
126 {.Z-}
127 public
128 constructor Create(AValSize : Cardinal; AHashSize : Integer); virtual;
129 {-Initialize an empty hash table}
130 destructor Destroy; override;
131 {-Destroy a hash table}
132
133 procedure LoadFromStream(S : TStream); override;
134 {-Read a hash table and its data from a stream}
135 procedure StoreToStream(S : TStream); override;
136 {-Write a hash table and its data to a stream}
137
138 procedure Clear; override;
139 {-Remove all nodes from container but leave it instantiated}
140
141 function Exists(const V; var Data : Pointer) : Boolean;
142 {-Return True and the Data pointer if V is in the hash table}
143 procedure Add(const V; Data : Pointer);
144 {-Add new value and Data to the hash table}
145 procedure Delete(const V);
146 {-Delete a value from the hash table}
147 procedure Update(const V; Data : Pointer);
148 {-Update the data for an existing element}
149 function Find(Data : Pointer; var V) : Boolean;
150 {-Return True and the element value that matches Data}
151
152 procedure Assign(Source: TPersistent); override;
153 {-Assign another hash table's contents to this one}
154 procedure Join(H : TStHashTable; IgnoreDups : Boolean);
155 {-Add hash table H into this one and dispose H}
156
157 function Iterate(Action : TIterateFunc;
158 OtherData : Pointer) : TStHashNode;
159 {-Call Action for all the nodes, returning the last node visited}
160
161 procedure NodeRemoved(const V; Data : Pointer); virtual;
162 {-Called when a not recently used node is removed from the table}
163
164 function BinCount(H : Integer) : LongInt;
165 {-Return number of names in a hash bin (for testing)}
166
167 property Equal : TUntypedCompareFunc
168 {-Change the string compare function; only for an empty table}
169 read FEqual
170 write htSetEqual;
171
172 property Hash : THashFunc
173 {-Change the hash function; only for an empty table}
174 read FHash
175 write htSetHash;
176
177 property HashSize : Integer
178 {-Change the hash table size; preserves existing elements}
179 read FHashSize
180 write htSetHashSize;
181
182 property ValSize : Cardinal
183 {-Read the size of each element in the table}
184 read FValSize;
185
186 property MaxNodes : LongInt
187 {-Change the maximum nodes in the table}
188 read FMaxNodes
189 write htSetMaxNodes;
190 end;
191
192 {======================================================================}
193
194 implementation
195
196 {$IFDEF ThreadSafe}
197 var
198 ClassCritSect : TRTLCriticalSection;
199 {$ENDIF}
200
201 procedure EnterClassCS;
202 begin
203 {$IFDEF ThreadSafe}
204 EnterCriticalSection(ClassCritSect);
205 {$ENDIF}
206 end;
207
208 procedure LeaveClassCS;
209 begin
210 {$IFDEF ThreadSafe}
211 LeaveCriticalSection(ClassCritSect);
212 {$ENDIF}
213 end;
214
215
216
217 {----------------------------------------------------------------------}
218
219 constructor TStHashNode.CreateNode(const AValue; AValSize : Cardinal;
220 AData : Pointer);
221 begin
222 Create(AData);
223 hnValSize := AValSize;
224 GetMem(hnValue, AValSize);
225 Move(AValue, hnValue^, AValSize);
226 end;
227
228 destructor TStHashNode.Destroy;
229 begin
230 if Assigned(hnValue) then
231 FreeMem(hnValue, hnValSize);
232 inherited Destroy;
233 end;
234
235 function TStHashNode.GetValue : Pointer;
236 begin
237 Result := hnValue;
238 end;
239
240 {----------------------------------------------------------------------}
241
242 procedure TStHashTable.Add(const V; Data : Pointer);
243 var
244 H : Integer;
245 P, T : TStHashNode;
246 begin
247 {$IFDEF ThreadSafe}
248 EnterCS;
249 try
250 {$ENDIF}
251 htFindNode(V, H, P, T);
252 if Assigned(T) then
253 RaiseContainerError(stscDupNode);
254 htInsertNode(H, TStHashNode.CreateNode(V, FValSize, Data));
255 {$IFDEF ThreadSafe}
256 finally
257 LeaveCS;
258 end;
259 {$ENDIF}
260 end;
261
262 function AssignNode(Container : TStContainer;
263 Node : TStNode;
264 OtherData : Pointer) : Boolean; far;
265 var
266 HashNode : TStHashNode absolute Node;
267 OurHashTbl : TStHashTable absolute OtherData;
268 begin
269 OurHashTbl.Add(HashNode.Value^, HashNode.Data);
270 Result := true;
271 end;
272
273 procedure TStHashTable.Assign(Source: TPersistent);
274 begin
275 {$IFDEF ThreadSafe}
276 EnterCS;
277 try
278 {$ENDIF}
279 {The only container that we allow to be assigned to a hash table
280 is... another hash table}
281 if (Source is TStHashTable) then begin
282 Clear;
283 FValSize := TStHashTable(Source).ValSize;
284 TStHashTable(Source).Iterate(AssignNode, Self);
285 end
286 else
287 inherited Assign(Source);
288 {$IFDEF ThreadSafe}
289 finally
290 LeaveCS;
291 end;
292 {$ENDIF}
293 end;
294
295
296 function TStHashTable.BinCount(H : Integer) : LongInt;
297 var
298 C : LongInt;
299 T : TStHashNode;
300 begin
301 {$IFDEF ThreadSafe}
302 EnterCS;
303 try
304 {$ENDIF}
305 C := 0;
306 T := htHeads^[H];
307 while Assigned(T) do begin
308 inc(C);
309 T := T.hnNext;
310 end;
311 Result := C;
312 {$IFDEF ThreadSafe}
313 finally
314 LeaveCS;
315 end;
316 {$ENDIF}
317 end;
318
319 procedure TStHashTable.Clear;
320 var
321 TableSize : Cardinal;
322 begin
323 {$IFDEF ThreadSafe}
324 EnterCS;
325 try
326 {$ENDIF}
327 if FCount <> 0 then begin
328 Iterate(DestroyNode, nil);
329 FCount := 0;
330 htLRU := 0;
331 TableSize := FHashSize*SizeOf(TStHashNode);
332 FillChar(htHeads^, TableSize, 0);
333 FillChar(htTails^, TableSize, 0);
334 end;
335 {$IFDEF ThreadSafe}
336 finally
337 LeaveCS;
338 end;
339 {$ENDIF}
340 end;
341
342 constructor TStHashTable.Create(AValSize : Cardinal; AHashSize : Integer);
343 begin
344 if AValSize = 0 then
345 RaiseContainerError(stscBadSize);
346
347 CreateContainer(TStHashNode, 0);
348
349 FValSize := AValSize;
350 FMaxNodes := MaxLongInt;
351
352 {allocate hash table by assigning to the HashSize property}
353 HashSize := AHashSize;
354 end;
355
356 procedure TStHashTable.Delete(const V);
357 var
358 H : Integer;
359 P, T : TStHashNode;
360 begin
361 {$IFDEF ThreadSafe}
362 EnterCS;
363 try
364 {$ENDIF}
365 htFindNode(V, H, P, T);
366 if Assigned(T) then begin
367 if Assigned(P) then
368 P.hnNext := T.hnNext
369 else
370 htHeads^[H] := T.hnNext;
371 if T = htTails^[H] then
372 htTails^[H] := P;
373 DestroyNode(Self, T, nil);
374 Dec(FCount);
375 end;
376 {$IFDEF ThreadSafe}
377 finally
378 LeaveCS;
379 end;
380 {$ENDIF}
381 end;
382
383 destructor TStHashTable.Destroy;
384 var
385 TableSize : Cardinal;
386 begin
387 if conNodeProt = 0 then
388 Clear;
389 TableSize := FHashSize*SizeOf(TStHashNode);
390 if Assigned(htHeads) then
391 FreeMem(htHeads, TableSize);
392 if Assigned(htTails) then
393 FreeMem(htTails, TableSize);
394 IncNodeProtection;
395 inherited Destroy;
396 end;
397
398 function TStHashTable.Exists(const V; var Data : Pointer) : Boolean;
399 var
400 H : Integer;
401 P, T : TStHashNode;
402 begin
403 {$IFDEF ThreadSafe}
404 EnterCS;
405 try
406 {$ENDIF}
407 htFindNode(V, H, P, T);
408 if Assigned(T) then begin
409 htMoveToFront(H, P, T);
410 htUpdateLRU(T);
411 Result := True;
412 Data := T.Data;
413 end else
414 Result := False;
415 {$IFDEF ThreadSafe}
416 finally
417 LeaveCS;
418 end;
419 {$ENDIF}
420 end;
421
422 function FindNodeData(Container : TStContainer; Node : TStNode;
423 OtherData : Pointer) : Boolean; far;
424 begin
425 Result := (OtherData <> Node.Data);
426 end;
427
428 function TStHashTable.Find(Data : Pointer; var V) : Boolean;
429 var
430 H : Integer;
431 P, T : TStHashNode;
432 begin
433 {$IFDEF ThreadSafe}
434 EnterCS;
435 try
436 {$ENDIF}
437 htIterate(FindNodeData, Data, H, P, T);
438 if Assigned(T) then begin
439 htMoveToFront(H, P, T);
440 htUpdateLRU(T);
441 Result := True;
442 Move(T.Value^, V, FValSize);
443 end else
444 Result := False;
445 {$IFDEF ThreadSafe}
446 finally
447 LeaveCS;
448 end;
449 {$ENDIF}
450 end;
451
452 procedure TStHashTable.htDeleteOldestNode;
453 {-Find and delete the hash node with the smallest LRU counter}
454 var
455 H, MinH : Integer;
456 MinLRU : LongInt;
457 T, P : TStHashNode;
458 begin
459 if FCount <> 0 then begin
460 MinLRU := MaxLongInt;
461 MinH := 0;
462 for H := 0 to FHashSize-1 do
463 if Assigned(htTails^[H]) and (htTails^[H].LRU <= MinLRU) then begin
464 MinH := H;
465 MinLRU := htTails^[H].LRU;
466 end;
467
468 {notify the application}
469 with htTails^[MinH] do
470 NodeRemoved(hnValue^, Data);
471
472 {destroy the node}
473 DestroyNode(Self, htTails^[MinH], nil);
474 dec(FCount);
475
476 {remove the node}
477 if htTails^[MinH] = htHeads^[MinH] then begin
478 {only node in this bin}
479 htTails^[MinH] := nil;
480 htHeads^[MinH] := nil;
481 end else begin
482 {at least two nodes in this bin}
483 T := htHeads^[MinH];
484 P := nil;
485 while T <> htTails^[MinH] do begin
486 P := T;
487 T := T.hnNext;
488 end;
489 P.hnNext := nil;
490 htTails^[MinH] := P;
491 end;
492 end;
493 end;
494
495 procedure TStHashTable.htFindNode(const V; var H : Integer;
496 var Prev, This : TStHashNode);
497 var
498 P, T : TStHashNode;
499 begin
500 if not(Assigned(FEqual) and Assigned(FHash)) then
501 RaiseContainerError(stscNoCompare);
502
503 Prev := nil;
504 This := nil;
505 H := FHash(V, HashSize);
506 T := htHeads^[H];
507 P := nil;
508 while Assigned(T) do begin
509 if FEqual(V, T.Value^) = 0 then begin
510 Prev := P;
511 This := T;
512 Exit;
513 end;
514 P := T;
515 T := T.hnNext;
516 end;
517
518 {not found}
519 This := nil;
520 end;
521
522 procedure TStHashTable.htInsertNode(H : Integer; This : TStHashNode);
523 {-Insert node This at front of hash bin H}
524 var
525 P : TStHashNode;
526 begin
527 P := htHeads^[H];
528 htHeads^[H] := This;
529 if not Assigned(htTails^[H]) then
530 htTails^[H] := This;
531 This.hnNext := P;
532 htUpdateLRU(This);
533 Inc(FCount);
534 if FCount > FMaxNodes then
535 htDeleteOldestNode;
536 end;
537
538 procedure TStHashTable.htIterate(Action : TIterateFunc; OtherData : Pointer;
539 var H : Integer; var Prev, This : TStHashNode);
540 {-Internal version of Iterate that returns more details}
541 var
542 AHash : Integer;
543 P, T, N : TStHashNode;
544 begin
545 if FCount <> 0 then begin
546 for AHash := 0 to FHashSize-1 do begin
547 T := htHeads^[AHash];
548 P := nil;
549 while Assigned(T) do begin
550 N := T.hnNext;
551 if Action(Self, T, OtherData) then begin
552 P := T;
553 T := N;
554 end else begin
555 H := AHash;
556 Prev := P;
557 This := T;
558 Exit;
559 end;
560 end;
561 end;
562 end;
563 This := nil;
564 end;
565
566 procedure TStHashTable.htMoveToFront(H : Integer; Prev, This : TStHashNode);
567 {-Move This to front of list}
568 begin
569 if Assigned(Prev) then begin
570 Prev.hnNext := This.hnNext;
571 This.hnNext := htHeads^[H];
572 htHeads^[H] := This;
573 if This = htTails^[H] then
574 htTails^[H] := Prev;
575 end;
576 end;
577
578 procedure TStHashTable.htSetEqual(E : TUntypedCompareFunc);
579 begin
580 {$IFDEF ThreadSafe}
581 EnterCS;
582 try
583 {$ENDIF}
584 if Count = 0 then
585 FEqual := E;
586 {$IFDEF ThreadSafe}
587 finally
588 LeaveCS;
589 end;
590 {$ENDIF}
591 end;
592
593 procedure TStHashTable.htSetHash(H : THashFunc);
594 begin
595 {$IFDEF ThreadSafe}
596 EnterCS;
597 try
598 {$ENDIF}
599 if Count = 0 then
600 FHash := H;
601 {$IFDEF ThreadSafe}
602 finally
603 LeaveCS;
604 end;
605 {$ENDIF}
606 end;
607
608 procedure TStHashTable.htSetHashSize(Size : Integer);
609 var
610 HInx : integer;
611 TableSize: LongInt;
612 Temp : TStHashNode;
613 Node : TStHashNode;
614 OldHeads : PHashArray;
615 OldTails : PHashArray;
616 OldSize : Integer;
617 OldCount : Integer;
618 OldDisposeData : TDisposeDataProc;
619 OldOnDisposeData : TStDisposeDataEvent;
620 begin
621 {$IFDEF ThreadSafe}
622 EnterCS;
623 try
624 {$ENDIF}
625 {calculate the new table size}
626 TableSize := LongInt(Size) * sizeof(TStHashNode);
627 if (Size <= 0) {or (TableSize > MaxBlockSize)} then
628 RaiseContainerError(stscBadSize);
629
630 {only do something if there's something to do}
631 if (Size <> FHashSize) then begin
632
633 {Notes: lots of things are going to be happening here: new
634 allocations, nodes copied from the old table to the new,
635 etc. Ideally if an exception is raised we would like to
636 restore the hash table to the state it was in
637 originally, before letting the exception escape}
638
639 {save enough data about the current state of the table to
640 allow restoring in case of an exception}
641 OldHeads := htHeads;
642 OldTails := htTails;
643 OldSize := FHashSize;
644 OldCount := FCount;
645 OldDisposeData := DisposeData;
646 OldOnDisposeData := OnDisposeData;
647
648 {reset Self's data}
649 htHeads := nil;
650 htTails := nil;
651 FHashSize := Size;
652 FCount := 0;
653 DisposeData := nil;
654 OnDisposeData := nil;
655
656 {from this point, exceptions can occur with impunity...}
657 try
658 {allocate the new head and tail tables}
659 htHeads := AllocMem(TableSize);
660 htTails := AllocMem(TableSize);
661
662 {if there is data to transfer, do so}
663 if (OldHeads <> nil) and (OldCount <> 0) then begin
664 for HInx := 0 to pred(OldSize) do begin
665 Node := OldHeads^[HInx];
666 while Assigned(Node) do begin
667 Add(Node.hnValue^, Node.Data);
668 Node := Node.hnNext;
669 end;
670 end;
671 end;
672
673 {now all the data has been transferred, we can
674 destroy the old table}
675 if (OldHeads <> nil) then begin
676 for HInx := 0 to pred(OldSize) do begin
677 Node := OldHeads^[HInx];
678 while Assigned(Node) do begin
679 Temp := Node;
680 Node := Node.hnNext;
681 Temp.Free;
682 end;
683 end;
684 FreeMem(OldHeads, OldSize * sizeof(TStHashNode));
685 end;
686 if (OldTails <> nil) then
687 FreeMem(OldTails, OldSize * sizeof(TStHashNode));
688
689 {restore the disposedata routines}
690 DisposeData := OldDisposeData;
691 OnDisposeData := OldOnDisposeData;
692
693 except
694 {destroy the new data}
695 if (htHeads <> nil) then begin
696 for HInx := 0 to pred(FHashSize) do begin
697 Node := htHeads^[HInx];
698 while Assigned(Node) do begin
699 Temp := Node;
700 Node := Node.hnNext;
701 Temp.Free;
702 end;
703 end;
704 FreeMem(htHeads, TableSize);
705 end;
706 if (htTails <> nil) then
707 FreeMem(htTails, TableSize);
708 {restore the old data}
709 htHeads := OldHeads;
710 htTails := OldTails;
711 FHashSize := OldSize;
712 FCount := OldCount;
713 DisposeData := OldDisposeData;
714 OnDisposeData := OldOnDisposeData;
715 {reraise the exception}
716 raise;
717 end;
718 end;
719 {$IFDEF ThreadSafe}
720 finally
721 LeaveCS;
722 end;
723 {$ENDIF}
724 end;
725
726 procedure TStHashTable.htSetMaxNodes(Nodes : LongInt);
727 begin
728 if Nodes < 1 then
729 RaiseContainerError(stscBadSize);
730 FMaxNodes := Nodes;
731 while FCount > FMaxNodes do
732 htDeleteOldestNode;
733 end;
734
735 type
736 TMinNode = record
737 MLRU : LongInt;
738 MNode : TStHashNode;
739 end;
740 PMinNode = ^TMinNode;
741
742 function FindMinPositiveNode(Container : TStContainer;
743 Node : TStNode;
744 OtherData : Pointer) : Boolean; far;
745 {-Used to find the smallest non-negative LRU in the table}
746 begin
747 with PMinNode(OtherData)^, TStHashNode(Node) do
748 if (LRU >= 0) and (LRU <= MLRU) then begin
749 MLRU := LRU;
750 MNode := TStHashNode(Node);
751 end;
752 Result := True;
753 end;
754
755 function NegateNodeLRU(Container : TStContainer;
756 Node : TStNode;
757 OtherData : Pointer) : Boolean; far;
758 {-Used to negate the LRU values of all nodes in the table}
759 begin
760 with TStHashNode(Node) do
761 LRU := -LRU;
762 Result := True;
763 end;
764
765 procedure TStHashTable.htUpdateLRU(This : TStHashNode);
766 {-Reassign all LRU values sequentially in their existing order}
767 var
768 MinNode : TMinNode;
769 begin
770 inc(htLRU);
771 This.LRU := htLRU;
772 if htLRU = MaxLongInt then begin
773 {scan table and pack LRU values}
774 htLRU := 0;
775 repeat
776 inc(htLRU);
777 MinNode.MLRU := MaxLongInt;
778 MinNode.MNode := nil;
779 Iterate(FindMinPositiveNode, @MinNode);
780 if not Assigned(MinNode.MNode) then
781 break;
782 {nodes already visited are set to a negative value}
783 {depends on never having an LRU of zero}
784 MinNode.MNode.LRU := -htLRU;
785 until False;
786 {negative values are made positive}
787 Iterate(NegateNodeLRU, nil);
788 end;
789 end;
790
791 function TStHashTable.Iterate(Action : TIterateFunc;
792 OtherData : Pointer) : TStHashNode;
793 var
794 H : Integer;
795 P : TStHashNode;
796 begin
797 {$IFDEF ThreadSafe}
798 EnterCS;
799 try
800 {$ENDIF}
801 htIterate(Action, OtherData, H, P, Result);
802 {$IFDEF ThreadSafe}
803 finally
804 LeaveCS;
805 end;
806 {$ENDIF}
807 end;
808
809 function JoinNode(Container : TStContainer;
810 Node : TStNode;
811 OtherData : Pointer) : Boolean; far;
812 {-Used to add nodes from another table into this one}
813 var
814 H : Integer;
815 P, T : TStHashNode;
816 begin
817 Result := True;
818 with TStHashTable(OtherData) do begin
819 htFindNode(TStHashNode(Node).Value^, H, P, T);
820 if Assigned(T) then
821 if htIgnoreDups then begin
822 Node.Free;
823 Exit;
824 end else
825 RaiseContainerError(stscDupNode);
826 htInsertNode(H, TStHashNode(Node));
827 end;
828 end;
829
830 procedure TStHashTable.Join(H : TStHashTable; IgnoreDups : Boolean);
831 begin
832 {$IFDEF ThreadSafe}
833 EnterClassCS;
834 EnterCS;
835 H.EnterCS;
836 try
837 {$ENDIF}
838 htIgnoreDups := IgnoreDups;
839 H.Iterate(JoinNode, Self);
840 {dispose of D, but not its nodes}
841 H.IncNodeProtection;
842 H.Free;
843 {$IFDEF ThreadSafe}
844 finally
845 H.LeaveCS;
846 LeaveCS;
847 LeaveClassCS;
848 end;
849 {$ENDIF}
850 end;
851
852 procedure TStHashTable.LoadFromStream(S : TStream);
853 var
854 Data, Value : Pointer;
855 AValSize : Cardinal;
856 Reader : TReader;
857 StreamedClass : TPersistentClass;
858 StreamedNodeClass : TPersistentClass;
859 StreamedClassName : string;
860 StreamedNodeClassName : string;
861 begin
862 {$IFDEF ThreadSafe}
863 EnterCS;
864 try
865 {$ENDIF}
866 Clear;
867 Reader := TReader.Create(S, 1024);
868 try
869 with Reader do begin
870 StreamedClassName := ReadString;
871 StreamedClass := GetClass(StreamedClassName);
872 if not Assigned(StreamedClass) then
873 RaiseContainerErrorFmt(stscUnknownClass, [StreamedClassName]);
874 if (StreamedClass <> Self.ClassType) then
875 RaiseContainerError(stscWrongClass);
876 StreamedNodeClassName := ReadString;
877 StreamedNodeClass := GetClass(StreamedNodeClassName);
878 if not Assigned(StreamedNodeClass) then
879 RaiseContainerErrorFmt(stscUnknownNodeClass, [StreamedNodeClassName]);
880 if (StreamedNodeClass <> conNodeClass) then
881 RaiseContainerError(stscWrongNodeClass);
882
883 AValSize := ReadInteger;
884 if AValSize <> FValSize then
885 RaiseContainerError(stscBadSize);
886 HashSize := ReadInteger;
887 FMaxNodes := ReadInteger;
888 GetMem(Value, FValSize);
889 try
890 ReadListBegin;
891 while not EndOfList do begin
892 ReadBoolean;
893 Read(Value^, FValSize);
894 Data := DoLoadData(Reader);
895 Add(Value^, Data);
896 end;
897 ReadListEnd;
898 finally
899 FreeMem(Value, FValSize);
900 end;
901 end;
902 finally
903 Reader.Free;
904 end;
905 {$IFDEF ThreadSafe}
906 finally
907 LeaveCS;
908 end;
909 {$ENDIF}
910 end;
911
912 procedure TStHashTable.NodeRemoved(const V; Data : Pointer);
913 begin
914 {does nothing by default}
915 end;
916
917 procedure TStHashTable.StoreToStream(S : TStream);
918 var
919 H : Integer;
920 Walker : TStHashNode;
921 Writer : TWriter;
922 begin
923 {$IFDEF ThreadSafe}
924 EnterCS;
925 try
926 {$ENDIF}
927 Writer := TWriter.Create(S, 1024);
928 try
929 with Writer do begin
930 WriteString(Self.ClassName);
931 WriteString(conNodeClass.ClassName);
932 WriteInteger(FValSize);
933 WriteInteger(FHashSize);
934 WriteInteger(FMaxNodes);
935 WriteListBegin;
936 if Count <> 0 then
937 for H := 0 to FHashSize-1 do begin
938 Walker := htHeads^[H];
939 while Assigned(Walker) do begin
940 {writing the True boolean prevents false termination of the
941 list if Value's first byte is zero when the stream is
942 loaded into another hash table}
943 WriteBoolean(True);
944 Write(Walker.Value^, FValSize);
945 DoStoreData(Writer, Walker.Data);
946 Walker := Walker.hnNext;
947 end;
948 end;
949 WriteListEnd;
950 end;
951 finally
952 Writer.Free;
953 end;
954 {$IFDEF ThreadSafe}
955 finally
956 LeaveCS;
957 end;
958 {$ENDIF}
959 end;
960
961 procedure TStHashTable.Update(const V; Data : Pointer);
962 var
963 H : Integer;
964 P, T : TStHashNode;
965 begin
966 {$IFDEF ThreadSafe}
967 EnterCS;
968 try
969 {$ENDIF}
970 htFindNode(V, H, P, T);
971 if Assigned(T) then begin
972 htMoveToFront(H, P, T);
973 htUpdateLRU(T);
974 T.Data := Data;
975 end;
976 {$IFDEF ThreadSafe}
977 finally
978 LeaveCS;
979 end;
980 {$ENDIF}
981 end;
982
983 {$IFDEF ThreadSafe}
984 initialization
985 Windows.InitializeCriticalSection(ClassCritSect);
986 finalization
987 Windows.DeleteCriticalSection(ClassCritSect);
988 {$ENDIF}
989 end.

  ViewVC Help
Powered by ViewVC 1.1.20