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

Contents of /dao/DelphiScanner/Components/tpsystools_4.04/source/StColl.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: 34996 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: 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