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

Annotation of /dao/DelphiScanner/Components/tpsystools_4.04/source/StColl.pas

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2671 - (hide annotations) (download)
Tue Aug 25 18:15:15 2015 UTC (8 years, 10 months ago) by torben
File size: 34996 byte(s)
Added tpsystools component
1 torben 2671 // 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: StColl.pas 4.04 *}
30     {*********************************************************}
31     {* SysTools: Huge, sparse collection class *}
32     {*********************************************************}
33    
34     {$I StDefine.inc}
35    
36     {Notes:
37     - STCOLL generally follows the standards set by Borland's TP6
38     TCollection. All elements in the collection are pointers. Elements can
39     be inserted, deleted, and accessed by index number. The size of the
40     collection grows dynamically as needed. However, STCOLL is implemented
41     in a different fashion that gives it more capacity and higher
42     efficiency in some ways.
43    
44     - STCOLL theoretically allows up to 2 billion elements. The collection
45     is "sparse" in the sense that most of the memory is allocated only
46     when a value is assigned to an element in the collection.
47    
48     - STCOLL is implemented as a linked list of pointers to pages. Each
49     page can hold a fixed number of collection elements, the size
50     being specified when the TStCollection is created. Only when an
51     element with a given index is written to is a page descriptor and a
52     page allocated for it. However, the first page is allocated when the
53     collection is created.
54    
55     - The larger the page size, the faster it is to access a given index
56     and the less memory overhead is used for management of the collection.
57     If the page size is at least as large as the number of elements added
58     to the collection, TStCollection works just like Borland's old
59     TCollection. Inserting elements in the middle of very large pages can
60     be slow, however, because lots of data must be shifted to make room
61     for each new element. Conversely, if the page size is 1, TStCollection
62     acts much like a traditional linked list.
63    
64     - The page size is limited to 16380 elements in 16-bit mode, or
65     536 million elements in 32-bit mode.
66    
67     - STCOLL uses the DisposeData procedure of TStContainer to determine
68     how to free elements in the collection. By default, it does nothing.
69    
70     - AtFree and Free do not exist in TStCollection. Instead the AtDelete
71     and Delete methods will also dispose of the element if the DisposeData
72     property of the class has been set.
73    
74     - The Count property returns the index (plus one) of the highest
75     element inserted or put.
76    
77     - AtInsert can insert an item at any index, even larger than Count+1.
78     AtPut also can put an item at any index.
79    
80     - If the At function is called for any non-negative index whose value
81     has not been explicitly assigned using Insert or AtInsert, it returns
82     nil.
83    
84     - For the non-sorted collection, IndexOf compares the data pointers
85     directly, for exact equality, without using any Comparison function.
86    
87     - TStSortedCollection allows duplicate nodes only if its Duplicates
88     property is set.
89    
90     - The Efficiency property returns a measure of how fully the collection
91     is using the memory pages it has allocated. It returns a number in the
92     range of 0 to 100 (percent). Calling TStSortedCollection.Insert,
93     AtInsert, Delete, or AtDelete can result in a low efficiency. After a
94     series of calls to these methods it is often worthwhile to call the
95     Pack method to increase the efficiency as much as possible.
96     }
97    
98     unit StColl;
99     {-}
100    
101     interface
102    
103     uses
104     Windows, Classes,
105    
106     StConst, StBase, StList;
107    
108     type
109     {.Z+}
110     PPointerArray = ^TPointerArray;
111     TPointerArray = array[0..(StMaxBlockSize div SizeOf(Pointer))-1] of Pointer;
112    
113     TPageDescriptor = class(TStListNode)
114     protected
115     {PageElements count is stored in inherited Data field}
116     pdPage : PPointerArray; {Pointer to page data}
117     pdStart : LongInt; {Index of first element in page}
118     pdCount : Integer; {Number of elements used in page}
119    
120     public
121     constructor Create(AData : Pointer); override;
122     destructor Destroy; override;
123     end;
124     {.Z-}
125    
126     TCollIterateFunc = function (Container : TStContainer;
127     Data : Pointer;
128     OtherData : Pointer) : Boolean;
129    
130     TStCollection = class(TStContainer)
131     {.Z+}
132     protected
133     colPageList : TStList; {List of page descriptors}
134     colPageElements : Integer; {Number of elements in a page}
135     colCachePage : TPageDescriptor; {Page last found by At}
136    
137     procedure colAdjustPagesAfter(N : TPageDescriptor; Delta : LongInt);
138     procedure colAtInsertInPage(N : TPageDescriptor; PageIndex : Integer;
139     AData : Pointer);
140     procedure colAtDeleteInPage(N : TPageDescriptor; PageIndex : Integer);
141     function colGetCount : LongInt;
142     function colGetEfficiency : Integer;
143    
144     procedure ForEachPointer(Action : TIteratePointerFunc; OtherData : pointer);
145     override;
146     function StoresPointers : boolean;
147     override;
148     {.Z-}
149     public
150     constructor Create(PageElements : Integer); virtual;
151     {-Initialize a collection with given page size and allocate first page}
152     destructor Destroy; override;
153     {-Free a collection}
154    
155     procedure LoadFromStream(S : TStream); override;
156     {-Load a collection's data from a stream}
157     procedure StoreToStream(S : TStream); override;
158     {-Write a collection and its data to a stream}
159    
160     procedure Clear; override;
161     {-Deallocate all pages and free all items}
162     procedure Assign(Source: TPersistent); override;
163     {-Assign another container's contents to this one}
164     procedure Pack;
165     {-Squeeze collection elements into the least memory possible}
166    
167     function At(Index : LongInt) : Pointer;
168     {-Return the element at a given index}
169     function IndexOf(Data : Pointer) : LongInt; virtual;
170     {-Return the index of the first item with given data}
171    
172     procedure AtInsert(Index : LongInt; Data : Pointer);
173     {-Insert a new element at a given index and move following items down}
174     procedure AtPut(Index : LongInt; Data : Pointer);
175     {-Replace element at given index with new data}
176     procedure Insert(Data : Pointer); virtual;
177     {-Insert item at the end of the collection}
178    
179     procedure AtDelete(Index : LongInt);
180     {-Remove element at a given index, move following items up, free element}
181     procedure Delete(Data : Pointer);
182     {-Delete the first item with the given data}
183    
184     function Iterate(Action : TCollIterateFunc; Up : Boolean;
185     OtherData : Pointer) : Pointer;
186     {-Call Action for all the non-nil elements, returning the last data}
187    
188     property Count : LongInt
189     {-Return the index of the highest assigned item, plus one}
190     read colGetCount;
191    
192     property Efficiency : Integer
193     {-Return the overall percent Efficiency of the pages}
194     read colGetEfficiency;
195    
196     property Items[Index : LongInt] : Pointer
197     {-Return the Index'th node, 0-based}
198     read At
199     write AtPut;
200     default;
201     end;
202    
203     {.Z+}
204     TSCSearch = (SCSPageEmpty,
205     SCSLessThanThisPage,
206     SCSInThisPageRange,
207     SCSFound,
208     SCSGreaterThanThisPage);
209     {.Z-}
210    
211     TStSortedCollection = class(TStCollection)
212     {.Z+}
213     protected
214     FDuplicates : Boolean;
215    
216     function scSearchPage(AData : Pointer; N : TPageDescriptor;
217     var PageIndex : Integer) : TSCSearch;
218    
219     procedure scSetDuplicates(D : Boolean);
220     {.Z-}
221     public
222     procedure LoadFromStream(S : TStream); override;
223     {-Load a sorted collection's data from a stream}
224     procedure StoreToStream(S : TStream); override;
225     {-Write a collection and its data to a stream}
226    
227     function IndexOf(Data : Pointer) : LongInt; override;
228     {-Return the index of the first item with given data}
229     procedure Insert(Data : Pointer); override;
230     {-Insert item in sorted position}
231     property Duplicates : Boolean
232     {-Determine whether sorted collection allows duplicate data}
233     read FDuplicates
234     write scSetDuplicates;
235     end;
236    
237     {======================================================================}
238    
239     implementation
240    
241     function AssignData(Container : TStContainer;
242     Data, OtherData : Pointer) : Boolean; far;
243     var
244     OurColl : TStCollection absolute OtherData;
245     begin
246     OurColl.Insert(Data);
247     Result := true;
248     end;
249    
250     constructor TPageDescriptor.Create(AData : Pointer);
251     begin
252     inherited Create(AData);
253     GetMem(pdPage, LongInt(Data)*SizeOf(Pointer));
254     FillChar(pdPage^, LongInt(Data)*SizeOf(Pointer), 0);
255     end;
256    
257     destructor TPageDescriptor.Destroy;
258     begin
259     if Assigned(pdPage) then
260     FreeMem(pdPage, LongInt(Data)*SizeOf(Pointer));
261     inherited Destroy;
262     end;
263    
264     {----------------------------------------------------------------------}
265    
266     procedure TStCollection.Assign(Source: TPersistent);
267     begin
268     {$IFDEF ThreadSafe}
269     EnterCS;
270     try
271     {$ENDIF}
272     {The only containers that we allow to be assigned to a collection are
273     - a SysTools linked list (TStList)
274     - a SysTools binary search tree (TStTree)
275     - another SysTools collection (TStCollection, TStSortedCollection)}
276     if not AssignPointers(Source, AssignData) then
277     inherited Assign(Source);
278     {$IFDEF ThreadSafe}
279     finally
280     LeaveCS;
281     end;{try..finally}
282     {$ENDIF}
283     end;
284    
285     function TStCollection.At(Index : LongInt) : Pointer;
286     var
287     Start : LongInt;
288     N : TPageDescriptor;
289     begin
290     {$IFDEF ThreadSafe}
291     EnterCS;
292     try
293     {$ENDIF}
294     if Index < 0 then
295     RaiseContainerError(stscBadIndex);
296    
297     N := colCachePage;
298     if Index >= N.pdStart then
299     {search up}
300     repeat
301     with N do begin
302     Start := pdStart;
303     if Index < Start then begin
304     {element has not been set}
305     colCachePage := N;
306     break;
307     end else if Index < Start+pdCount then begin
308     {element is in this page}
309     colCachePage := N;
310     Result := pdPage^[Index-Start];
311     Exit;
312     end;
313     end;
314     N := TPageDescriptor(N.FNext);
315     until not Assigned(N)
316    
317     else begin
318     {search down}
319     N := TPageDescriptor(N.FPrev);
320     while Assigned(N) do begin
321     with N do begin
322     Start := pdStart;
323     if (Index >= Start+pdCount) then begin
324     {element has not been set}
325     colCachePage := N;
326     break;
327     end else if Index >= Start then begin
328     {element is in this page}
329     colCachePage := N;
330     Result := pdPage^[Index-Start];
331     Exit;
332     end;
333     end;
334     N := TPageDescriptor(N.FPrev);
335     end;
336     end;
337    
338     {not found, leave cache page unchanged}
339     Result := nil;
340     {$IFDEF ThreadSafe}
341     finally
342     LeaveCS;
343     end;
344     {$ENDIF}
345     end;
346    
347     procedure TStCollection.AtDelete(Index : LongInt);
348     var
349     Start : LongInt;
350     N : TPageDescriptor;
351     begin
352     {$IFDEF ThreadSafe}
353     EnterCS;
354     try
355     {$ENDIF}
356     if Index < 0 then
357     RaiseContainerError(stscBadIndex);
358    
359     N := colCachePage;
360     if Index >= N.pdStart then
361     repeat
362     with N do begin
363     Start := pdStart;
364     if Index < Start then begin
365     {element has not been set, nothing to free}
366     Dec(pdStart);
367     colAdjustPagesAfter(N, -1);
368     colCachePage := N;
369     Exit;
370     end else if Index < Start+pdCount then begin
371     {element is in this page}
372     colCachePage := N;
373     colAtDeleteInPage(N, Index-Start);
374     Exit;
375     end;
376     end;
377     N := TPageDescriptor(N.FNext);
378     until not Assigned(N)
379    
380     else begin
381     {search down}
382     N := TPageDescriptor(N.FPrev);
383     while Assigned(N) do begin
384     with N do begin
385     Start := pdStart;
386     if Index >= Start+pdCount then begin
387     {element has not been set, nothing to free}
388     Dec(pdStart);
389     colAdjustPagesAfter(N, -1);
390     colCachePage := N;
391     Exit;
392     end else if Index >= Start then begin
393     {element is in this page}
394     colCachePage := N;
395     colAtDeleteInPage(N, Index-Start);
396     Exit;
397     end;
398     end;
399     N := TPageDescriptor(N.FPrev);
400     end;
401     end;
402    
403     {index not found, nothing to delete}
404     {$IFDEF ThreadSafe}
405     finally
406     LeaveCS;
407     end;
408     {$ENDIF}
409     end;
410    
411     procedure TStCollection.AtInsert(Index : LongInt; Data : Pointer);
412     var
413     Start : LongInt;
414     NC : Integer;
415     N : TPageDescriptor;
416     begin
417     {$IFDEF ThreadSafe}
418     EnterCS;
419     try
420     {$ENDIF}
421     if Index < 0 then
422     RaiseContainerError(stscBadIndex);
423    
424     N := TPageDescriptor(colPageList.Head);
425     while Assigned(N) do begin
426     Start := N.pdStart;
427     if Index < Start then begin
428     {current page has indexes greater than the specified one}
429     if Start-Index <= colPageElements-N.pdCount then begin
430     {room to squeeze element into this page}
431     NC := Start-Index;
432     Move(N.pdPage^[0], N.pdPage^[NC], N.pdCount*SizeOf(Pointer));
433     FillChar(N.pdPage^[1], (NC-1)*SizeOf(Pointer), 0);
434     Inc(N.pdCount, NC);
435     end else begin
436     {insert on a new page before this one}
437     N := TPageDescriptor(colPageList.PlaceBefore(Pointer(colPageElements), N));
438     N.pdCount := 1;
439     end;
440     N.pdStart := Index;
441     N.pdPage^[0] := Data;
442     colAdjustPagesAfter(N, +1);
443     Exit;
444     end else if Index < Start+colPageElements then
445     if (not Assigned(N.FNext)) or (Index < TPageDescriptor(N.FNext).pdStart) then begin
446     {should be inserted on this page}
447     colAtInsertInPage(N, Index-Start, Data);
448     Exit;
449     end;
450     N := TPageDescriptor(N.FNext);
451     end;
452    
453     {should be inserted after all existing pages}
454     N := TPageDescriptor(colPageList.Append(Pointer(colPageElements)));
455     N.pdStart := Index;
456     N.pdCount := 1;
457     N.pdPage^[0] := Data;
458     {$IFDEF ThreadSafe}
459     finally
460     LeaveCS;
461     end;
462     {$ENDIF}
463     end;
464    
465     procedure TStCollection.AtPut(Index : LongInt; Data : Pointer);
466     var
467     Start : LongInt;
468     N, T : TPageDescriptor;
469     begin
470     {$IFDEF ThreadSafe}
471     EnterCS;
472     try
473     {$ENDIF}
474     if Index < 0 then
475     RaiseContainerError(stscBadIndex);
476    
477     {special case for putting to end of collection}
478     T := TPageDescriptor(colPageList.Tail);
479     if Index = T.pdStart+T.pdCount then begin
480     if T.pdCount >= colPageElements then begin
481     {last page is full, add another}
482     Start := T.pdStart+colPageElements;
483     T := TPageDescriptor(colPageList.Append(Pointer(colPageElements)));
484     T.pdStart := Start;
485     {T.pdCount := 0;}
486     end;
487     T.pdPage^[T.pdCount] := Data;
488     inc(T.pdCount);
489     Exit;
490     end;
491    
492     N := colCachePage;
493     if Index >= N.pdStart then
494     {search up}
495     repeat
496     Start := N.pdStart;
497     if Index < Start then begin
498     {element has not been set before}
499     N := TPageDescriptor(colPageList.PlaceBefore(Pointer(colPageElements), N));
500     N.pdStart := Index;
501     N.pdCount := 1;
502     N.pdPage^[0] := Data;
503     colCachePage := N;
504     Exit;
505     end else if Index < Start+N.pdCount then begin
506     {element fits in this page}
507     colCachePage := N;
508     N.pdPage^[Index-Start] := Data;
509     Exit;
510     end else if (N = T) and (Index < Start+colPageElements) then begin
511     {element fits in last page}
512     colCachePage := N;
513     N.pdPage^[Index-Start] := Data;
514     N.pdCount := Index-Start+1;
515     Exit;
516     end;
517     N := TPageDescriptor(N.FNext);
518     until not Assigned(N)
519    
520     else begin
521     {search down}
522     N := TPageDescriptor(N.FPrev);
523     while Assigned(N) do begin
524     Start := N.pdStart;
525     if (Index >= Start+N.pdCount) then begin
526     {element has not been set before}
527     N := TPageDescriptor(colPageList.PlaceBefore(Pointer(colPageElements), N));
528     N.pdStart := Index;
529     N.pdCount := 1;
530     N.pdPage^[0] := Data;
531     colCachePage := N;
532     Exit;
533     end else if Index >= Start then begin
534     {element is in this page}
535     colCachePage := N;
536     N.pdPage^[Index-Start] := Data;
537     Exit;
538     end;
539     N := TPageDescriptor(N.FPrev);
540     end;
541     end;
542    
543     {an element after all existing ones}
544     N := TPageDescriptor(colPageList.Append(Pointer(colPageElements)));
545     colCachePage := N;
546     N.pdStart := Index;
547     N.pdCount := 1;
548     N.pdPage^[0] := Data;
549     Exit;
550     {$IFDEF ThreadSafe}
551     finally
552     LeaveCS;
553     end;
554     {$ENDIF}
555     end;
556    
557     procedure TStCollection.Clear;
558     var
559     I : Integer;
560     N, P : TPageDescriptor;
561     begin
562     {$IFDEF ThreadSafe}
563     EnterCS;
564     try
565     {$ENDIF}
566     N := TPageDescriptor(colPageList.Head);
567     colCachePage := N;
568     while Assigned(N) do begin
569     for I := 0 to N.pdCount-1 do
570     DoDisposeData(N.pdPage^[I]);
571     P := TPageDescriptor(N.FNext);
572     if N = colCachePage then begin
573     {keep the first page, which is now empty}
574     N.pdCount := 0;
575     N.pdStart := 0;
576     end else
577     {delete all other pages}
578     colPageList.Delete(N);
579     N := P;
580     end;
581     {$IFDEF ThreadSafe}
582     finally
583     LeaveCS;
584     end;
585     {$ENDIF}
586     end;
587    
588     procedure TStCollection.colAdjustPagesAfter(N : TPageDescriptor; Delta : LongInt);
589     begin
590     N := TPageDescriptor(N.FNext);
591     while Assigned(N) do begin
592     inc(N.pdStart, Delta);
593     N := TPageDescriptor(N.FNext);
594     end;
595     end;
596    
597     procedure TStCollection.colAtDeleteInPage(N : TPageDescriptor; PageIndex : Integer);
598     begin
599     with N do begin
600     {free the element}
601     DoDisposeData(pdPage^[PageIndex]);
602     Move(pdPage^[PageIndex+1], pdPage^[PageIndex],
603     (colPageElements-PageIndex-1)*SizeOf(Pointer));
604     Dec(pdCount);
605     colAdjustPagesAfter(N, -1);
606     if (pdCount = 0) and (colPageList.Count > 1) then begin
607     {delete page if at least one page will remain}
608     if N = colCachePage then begin
609     colCachePage := TPageDescriptor(colPageList.Head);
610     if N = colCachePage then
611     colCachePage := TPageDescriptor(N.FNext);
612     end;
613     colPageList.Delete(N);
614     end;
615     end;
616     end;
617    
618     procedure TStCollection.colAtInsertInPage(N : TPageDescriptor; PageIndex : Integer;
619     AData : Pointer);
620     var
621     P : TPageDescriptor;
622     PC : Integer;
623     begin
624     with N do
625     if pdCount >= colPageElements then begin
626     {page is full, add another}
627     P := TPageDescriptor(colPageList.Place(Pointer(colPageElements), N));
628     {new page starts with element after the new one}
629     P.pdStart := pdStart+PageIndex+1;
630     PC := colPageElements-PageIndex;
631     Move(pdPage^[PageIndex], P.pdPage^[0], PC*SizeOf(Pointer));
632     pdPage^[PageIndex] := AData;
633     pdCount := PageIndex+1;
634     P.pdCount := PC;
635     colAdjustPagesAfter(P, +1);
636     end else begin
637     {room to add on this page}
638     if pdCount > PageIndex then begin
639     Move(pdPage^[PageIndex], pdPage^[PageIndex+1], (pdCount-PageIndex)*SizeOf(Pointer));
640     colAdjustPagesAfter(N, +1);
641     inc(pdCount);
642     end else begin
643     FillChar(pdPage^[pdCount], (PageIndex-pdCount)*SizeOf(Pointer), 0);
644     colAdjustPagesAfter(N, PageIndex+1-pdCount);
645     pdCount := PageIndex+1;
646     end;
647     pdPage^[PageIndex] := AData;
648     end;
649     end;
650    
651     function TStCollection.colGetCount : LongInt;
652     begin
653     {$IFDEF ThreadSafe}
654     EnterCS;
655     try
656     {$ENDIF}
657     with TPageDescriptor(colPageList.Tail) do
658     Result := pdStart+pdCount;
659     {$IFDEF ThreadSafe}
660     finally
661     LeaveCS;
662     end;
663     {$ENDIF}
664     end;
665    
666     function TStCollection.colGetEfficiency : Integer;
667     var
668     Pages, ECount : LongInt;
669     N : TPageDescriptor;
670     begin
671     {$IFDEF ThreadSafe}
672     EnterCS;
673     try
674     {$ENDIF}
675     ECount := 0;
676     Pages := 0;
677     N := TPageDescriptor(colPageList.Head);
678     while Assigned(N) do begin
679     with N do begin
680     inc(Pages);
681     inc(ECount, N.pdCount);
682     end;
683     N := TPageDescriptor(N.FNext);
684     end;
685     Result := (100*ECount) div (Pages*colPageElements);
686     {$IFDEF ThreadSafe}
687     finally
688     LeaveCS;
689     end;
690     {$ENDIF}
691     end;
692    
693     procedure TStCollection.ForEachPointer(Action : TIteratePointerFunc;
694     OtherData : pointer);
695     var
696     I : Integer;
697     N : TPageDescriptor;
698     begin
699     {$IFDEF ThreadSafe}
700     EnterCS;
701     try
702     {$ENDIF}
703     N := TPageDescriptor(colPageList.Head);
704     while Assigned(N) do begin
705     with N do
706     for I := 0 to pdCount-1 do
707     if (pdPage^[I] <> nil) then
708     if not Action(Self, pdPage^[I], OtherData) then begin
709     Exit;
710     end;
711     N := TPageDescriptor(N.FNext);
712     end;
713     {$IFDEF ThreadSafe}
714     finally
715     LeaveCS;
716     end;
717     {$ENDIF}
718     end;
719    
720     function TStCollection.StoresPointers : boolean;
721     begin
722     Result := true;
723     end;
724    
725     constructor TStCollection.Create(PageElements : Integer);
726     begin
727     CreateContainer(TStNode, 0);
728    
729     if (PageElements = 0) then
730     RaiseContainerError(stscBadSize);
731    
732     colPageList := TStList.Create(TPageDescriptor);
733     colPageElements := PageElements;
734    
735     {start with one empty page}
736     colPageList.Append(Pointer(colPageElements));
737     colCachePage := TPageDescriptor(colPageList.Head);
738     end;
739    
740     procedure TStCollection.Delete(Data : Pointer);
741     var
742     Index : LongInt;
743     begin
744     {$IFDEF ThreadSafe}
745     EnterCS;
746     try
747     {$ENDIF}
748     Index := IndexOf(Data);
749     if Index >= 0 then
750     AtDelete(Index);
751     {$IFDEF ThreadSafe}
752     finally
753     LeaveCS;
754     end;
755     {$ENDIF}
756     end;
757    
758     destructor TStCollection.Destroy;
759     begin
760     Clear;
761     colPageList.Free;
762     IncNodeProtection;
763     inherited Destroy;
764     end;
765    
766     function TStCollection.IndexOf(Data : Pointer) : LongInt;
767     var
768     I : LongInt;
769     N : TPageDescriptor;
770     begin
771     {$IFDEF ThreadSafe}
772     EnterCS;
773     try
774     {$ENDIF}
775     N := TPageDescriptor(colPageList.Head);
776     while Assigned(N) do begin
777     for I := 0 to N.pdCount-1 do
778     if N.pdPage^[I] = Data then begin
779     colCachePage := N;
780     Result := N.pdStart+I;
781     Exit;
782     end;
783     N := TPageDescriptor(N.FNext);
784     end;
785     IndexOf := -1;
786     {$IFDEF ThreadSafe}
787     finally
788     LeaveCS;
789     end;
790     {$ENDIF}
791     end;
792    
793     procedure TStCollection.Insert(Data : Pointer);
794     var
795     Start : LongInt;
796     N : TPageDescriptor;
797     begin
798     {$IFDEF ThreadSafe}
799     EnterCS;
800     try
801     {$ENDIF}
802     N := TPageDescriptor(colPageList.Tail);
803     if N.pdCount >= colPageElements then begin
804     {last page is full, add another}
805     Start := N.pdStart+colPageElements;
806     N := TPageDescriptor(colPageList.Append(Pointer(colPageElements)));
807     N.pdStart := Start;
808     {N.pdCount := 0;}
809     end;
810     N.pdPage^[N.pdCount] := Data;
811     inc(N.pdCount);
812     {$IFDEF ThreadSafe}
813     finally
814     LeaveCS;
815     end;
816     {$ENDIF}
817     end;
818    
819     function TStCollection.Iterate(Action : TCollIterateFunc; Up : Boolean;
820     OtherData : Pointer) : Pointer;
821     var
822     I : Integer;
823     N : TPageDescriptor;
824     begin
825     {$IFDEF ThreadSafe}
826     EnterCS;
827     try
828     {$ENDIF}
829     if Up then begin
830     N := TPageDescriptor(colPageList.Head);
831     while Assigned(N) do begin
832     with N do
833     for I := 0 to pdCount-1 do
834     if (pdPage^[I] <> nil) then
835     if not Action(Self, pdPage^[I], OtherData) then begin
836     Result := pdPage^[I];
837     Exit;
838     end;
839     N := TPageDescriptor(N.FNext);
840     end;
841     end else begin
842     N := TPageDescriptor(colPageList.Tail);
843     while Assigned(N) do begin
844     with N do
845     for I := pdCount-1 downto 0 do
846     if (pdPage^[I] <> nil) then
847     if not Action(Self, pdPage^[I], OtherData) then begin
848     Result := pdPage^[I];
849     Exit;
850     end;
851     N := TPageDescriptor(N.FPrev);
852     end;
853     end;
854    
855     Result := nil;
856     {$IFDEF ThreadSafe}
857     finally
858     LeaveCS;
859     end;
860     {$ENDIF}
861     end;
862    
863     procedure TStCollection.Pack;
864     var
865     N, P : TPageDescriptor;
866     NC : Integer;
867     begin
868     {$IFDEF ThreadSafe}
869     EnterCS;
870     try
871     {$ENDIF}
872     colCachePage := TPageDescriptor(colPageList.Head);
873     N := colCachePage;
874     while Assigned(N) do begin
875     while Assigned(N.FNext) and (N.pdCount < colPageElements) do begin
876     {there is a page beyond this page and room to add to this page}
877     P := TPageDescriptor(N.FNext);
878     if N.pdStart+N.pdCount = P.pdStart then begin
879     {next page has contiguous elements}
880     NC := colPageElements-N.pdCount;
881     if NC > P.pdCount then
882     NC := P.pdCount;
883     move(P.pdPage^[0], N.pdPage^[N.pdCount], NC*SizeOf(Pointer));
884     move(P.pdPage^[NC], P.pdPage^[0], (P.pdCount-NC)*SizeOf(Pointer));
885     inc(N.pdCount, NC);
886     dec(P.pdCount, NC);
887     if P.pdCount = 0 then
888     colPageList.Delete(P)
889     else
890     inc(P.pdStart, NC);
891     end else
892     {pages aren't contiguous, can't merge}
893     break;
894     end;
895     N := TPageDescriptor(N.FNext);
896     end;
897     {$IFDEF ThreadSafe}
898     finally
899     LeaveCS;
900     end;
901     {$ENDIF}
902     end;
903    
904     procedure TStCollection.LoadFromStream(S : TStream);
905     var
906     Data : pointer;
907     Reader : TReader;
908     PageElements : integer;
909     Index : longint;
910     StreamedClass : TPersistentClass;
911     StreamedClassName : string;
912     begin
913     Clear;
914     Reader := TReader.Create(S, 1024);
915     try
916     with Reader do
917     begin
918     StreamedClassName := ReadString;
919     StreamedClass := GetClass(StreamedClassName);
920     if (StreamedClass = nil) then
921     RaiseContainerErrorFmt(stscUnknownClass, [StreamedClassName]);
922     if (not IsOrInheritsFrom(StreamedClass, Self.ClassType)) or
923     (not IsOrInheritsFrom(TStCollection, StreamedClass)) then
924     RaiseContainerError(stscWrongClass);
925     PageElements := ReadInteger;
926     if (PageElements <> colPageElements) then
927     begin
928     colPageList.Clear;
929     colPageElements := PageElements;
930     colPageList.Append(Pointer(colPageElements));
931     colCachePage := TPageDescriptor(colPageList.Head);
932     end;
933     ReadListBegin;
934     while not EndOfList do
935     begin
936     Index := ReadInteger;
937     Data := DoLoadData(Reader);
938     AtPut(Index, Data);
939     end;
940     ReadListEnd;
941     end;
942     finally
943     Reader.Free;
944     end;
945     end;
946    
947     procedure TStCollection.StoreToStream(S : TStream);
948     var
949     Writer : TWriter;
950     N : TPageDescriptor;
951     i : integer;
952     begin
953     Writer := TWriter.Create(S, 1024);
954     try
955     with Writer do
956     begin
957     WriteString(Self.ClassName);
958     WriteInteger(colPageElements);
959     WriteListBegin;
960     N := TPageDescriptor(colPageList.Head);
961     while Assigned(N) do
962     begin
963     with N do
964     for i := 0 to pdCount-1 do
965     if (pdPage^[i] <> nil) then
966     begin
967     WriteInteger(pdStart + i);
968     DoStoreData(Writer, pdPage^[i]);
969     end;
970     N := TPageDescriptor(N.FNext);
971     end;
972     WriteListEnd;
973     end;
974     finally
975     Writer.Free;
976     end;
977     end;
978    
979     {----------------------------------------------------------------------}
980    
981     function TStSortedCollection.IndexOf(Data : Pointer) : LongInt;
982     var
983     N : TPageDescriptor;
984     PageIndex : Integer;
985     begin
986     {$IFDEF ThreadSafe}
987     EnterCS;
988     try
989     {$ENDIF}
990     if (Count = 0) then begin
991     Result := -1;
992     Exit;
993     end;
994     N := colCachePage;
995     if DoCompare(Data, N.pdPage^[0]) >= 0 then begin
996     {search up}
997     repeat
998     case scSearchPage(Data, N, PageIndex) of
999     SCSFound :
1000     begin
1001     colCachePage := N;
1002     Result := N.pdStart+PageIndex;
1003     Exit;
1004     end;
1005     SCSGreaterThanThisPage :
1006     {keep on searching} ;
1007     else
1008     {can't be anywhere else in the collection}
1009     break;
1010     end;
1011     N := TPageDescriptor(N.FNext);
1012     until not Assigned(N);
1013    
1014     end else begin
1015     {search down}
1016     N := TPageDescriptor(N.FPrev);
1017     while Assigned(N) do begin
1018     case scSearchPage(Data, N, PageIndex) of
1019     SCSFound :
1020     begin
1021     colCachePage := N;
1022     Result := N.pdStart+PageIndex;
1023     Exit;
1024     end;
1025     SCSLessThanThisPage :
1026     {keep on searching} ;
1027     else
1028     {can't be anywhere else in the collection}
1029     break;
1030     end;
1031     N := TPageDescriptor(N.FPrev);
1032     end;
1033     end;
1034    
1035     Result := -1;
1036     {$IFDEF ThreadSafe}
1037     finally
1038     LeaveCS;
1039     end;
1040     {$ENDIF}
1041     end;
1042    
1043     procedure TStSortedCollection.Insert(Data : Pointer);
1044     var
1045     N : TPageDescriptor;
1046     PageIndex : Integer;
1047     begin
1048     {$IFDEF ThreadSafe}
1049     EnterCS;
1050     try
1051     {$ENDIF}
1052     N := TPageDescriptor(colPageList.Head);
1053     while Assigned(N) do begin
1054     case scSearchPage(Data, N, PageIndex) of
1055     SCSPageEmpty, SCSInThisPageRange, SCSLessThanThisPage :
1056     begin
1057     colAtInsertInPage(N, PageIndex, Data);
1058     Exit;
1059     end;
1060     SCSFound :
1061     if FDuplicates then begin
1062     colAtInsertInPage(N, PageIndex, Data);
1063     Exit;
1064     end else
1065     RaiseContainerError(stscDupNode);
1066     end;
1067     N := TPageDescriptor(N.FNext);
1068     end;
1069    
1070     {greater than all other items}
1071     inherited Insert(Data);
1072     {$IFDEF ThreadSafe}
1073     finally
1074     LeaveCS;
1075     end;
1076     {$ENDIF}
1077     end;
1078    
1079     function TStSortedCollection.scSearchPage(AData : Pointer; N : TPageDescriptor;
1080     var PageIndex : Integer) : TSCSearch;
1081     var
1082     L, R, M, Comp : Integer;
1083     begin
1084     with N do
1085     if pdCount = 0 then begin
1086     Result := SCSPageEmpty;
1087     PageIndex := 0;
1088     end else if DoCompare(AData, pdPage^[0]) < 0 then begin
1089     Result := SCSLessThanThisPage;
1090     PageIndex := 0;
1091     end else if DoCompare(AData, pdPage^[pdCount-1]) > 0 then
1092     Result := SCSGreaterThanThisPage
1093     else begin
1094     {data might be in this page, check using binary search}
1095     Result := SCSInThisPageRange;
1096     L := 0;
1097     R := pdCount-1;
1098     repeat
1099     M := (L+R) div 2;
1100     Comp := DoCompare(AData, pdPage^[M]);
1101     if Comp > 0 then
1102     L := M+1
1103     else begin
1104     R := M-1;
1105     if Comp = 0 then begin
1106     PageIndex := M;
1107     Result := SCSFound;
1108     if not FDuplicates then
1109     {force exit from repeat loop}
1110     L := M;
1111     {else loop to find first of a group of duplicate nodes}
1112     end;
1113     end;
1114     until L > R;
1115    
1116     if Result = SCSInThisPageRange then begin
1117     {not found in page, return where it would be inserted}
1118     PageIndex := M;
1119     if Comp > 0 then
1120     inc(PageIndex);
1121     end;
1122     end;
1123     end;
1124    
1125     procedure TStSortedCollection.scSetDuplicates(D : Boolean);
1126     begin
1127     if FDuplicates <> D then
1128     if D then
1129     FDuplicates := True
1130     else if FCount <> 0 then
1131     RaiseContainerError(stscBadDups)
1132     else
1133     FDuplicates := False;
1134     end;
1135    
1136     procedure TStSortedCollection.LoadFromStream(S : TStream);
1137     var
1138     Data : pointer;
1139     Reader : TReader;
1140     PageElements : integer;
1141     StreamedClass : TPersistentClass;
1142     StreamedClassName : string;
1143     begin
1144     Clear;
1145     Reader := TReader.Create(S, 1024);
1146     try
1147     with Reader do
1148     begin
1149     StreamedClassName := ReadString;
1150     StreamedClass := GetClass(StreamedClassName);
1151     if (StreamedClass = nil) then
1152     RaiseContainerErrorFmt(stscUnknownClass, [StreamedClassName]);
1153     if (not IsOrInheritsFrom(StreamedClass, Self.ClassType)) or
1154     (not IsOrInheritsFrom(TStCollection, StreamedClass)) then
1155     RaiseContainerError(stscWrongClass);
1156     PageElements := ReadInteger;
1157     if (PageElements <> colPageElements) then
1158     begin
1159     colPageList.Clear;
1160     colPageElements := PageElements;
1161     colPageList.Append(Pointer(colPageElements));
1162     colCachePage := TPageDescriptor(colPageList.Head);
1163     end;
1164     FDuplicates := ReadBoolean;
1165     ReadListBegin;
1166     while not EndOfList do
1167     begin
1168     ReadInteger; {read & discard index number}
1169     Data := DoLoadData(Reader);
1170     Insert(Data);
1171     end;
1172     ReadListEnd;
1173     end;
1174     finally
1175     Reader.Free;
1176     end;
1177     end;
1178    
1179     procedure TStSortedCollection.StoreToStream(S : TStream);
1180     var
1181     Writer : TWriter;
1182     N : TPageDescriptor;
1183     i : integer;
1184     begin
1185     Writer := TWriter.Create(S, 1024);
1186     try
1187     with Writer do
1188     begin
1189     WriteString(Self.ClassName);
1190     WriteInteger(colPageElements);
1191     WriteBoolean(FDuplicates);
1192     WriteListBegin;
1193     N := TPageDescriptor(colPageList.Head);
1194     while Assigned(N) do
1195     begin
1196     with N do
1197     for i := 0 to pdCount-1 do
1198     if (pdPage^[i] <> nil) then
1199     begin
1200     WriteInteger(pdStart + i);
1201     DoStoreData(Writer, pdPage^[i]);
1202     end;
1203     N := TPageDescriptor(N.FNext);
1204     end;
1205     WriteListEnd;
1206     end;
1207     finally
1208     Writer.Free;
1209     end;
1210     end;
1211    
1212    
1213     end.

  ViewVC Help
Powered by ViewVC 1.1.20