source: trunk/Packages/TemplateGenerics/Generic/GenericList.inc

Last change on this file was 30, checked in by chronos, 12 years ago
  • Updated: Component versions.
  • Added: Missing forms.
File size: 18.0 KB
Line 
1{$IFDEF INTERFACE}
2
3 // TGList implemented using templates
4 // - item operations (Add, Insert, ReplaceArray, Get, Set, IndexOf,
5 // Extract, Delete, Exchange)
6 // - item range operations (DeleteItems, InsertItems, ReplaceItems,
7 // Move, Fill)
8 // - other TGList operations (AddList, InsertList,
9 // ReplaceList, GetList, IndexOfList)
10 // - dynamic array operations (AddArray, InsertArray,
11 // ReplaceArray, GetArray, IndexOfArray)
12 // - all items operations (Clear, Reverse, Sort)
13
14 TGList = class;
15
16 TGListSortCompare = function(Item1, Item2: TGListItem): Integer of object;
17 TGListToStringConverter = function(Item: TGListItem): string;
18 TGListFromStringConverter = function(Text: string): TGListItem;
19 TGListItemArray = array of TGListItem;
20
21 // TGList<TGListIndex, TGListItem> = class
22 TGList = class
23 private
24 FItems: array of TGListItem;
25 FCount: TGListIndex;
26 FUpdateCount: Integer;
27 FOnUpdate: TNotifyEvent;
28 function Get(Index: TGListIndex): TGListItem;
29 function GetCapacity: TGListIndex;
30 function GetLast: TGListItem;
31 function GetFirst: TGListItem;
32 procedure SetCapacity(const AValue: TGListIndex);
33 procedure SetCapacityOptimized(const NewCapacity: TGListIndex);
34 procedure SetLast(AValue: TGListItem);
35 procedure SetFirst(AValue: TGListItem);
36 procedure QuickSort(L, R : TGListIndex; Compare: TGListSortCompare);
37 protected
38 procedure Put(Index: TGListIndex; const AValue: TGListItem); virtual;
39 procedure SetCount(const AValue: TGListIndex); virtual;
40 public
41 type
42 PItem = ^TGListItem;
43 function CompareMem(P1, P2: Pointer; Length: cardinal): Boolean; inline;
44 function Add(Item: TGListItem): TGListIndex;
45 procedure AddArray(Values: array of TGListItem);
46 procedure AddList(List: TGList);
47 procedure AddListPart(List: TGList; ItemIndex, ItemCount: TGListIndex);
48 procedure Assign(Source: TGList); virtual;
49 constructor Create; virtual;
50 procedure Clear; virtual;
51 procedure Delete(Index: TGListIndex); virtual;
52 procedure DeleteItems(Index, Count: TGListIndex);
53 function EqualTo(List: TGList): Boolean;
54 procedure Exchange(Index1, Index2: TGListIndex);
55 procedure Explode(Text, Separator: string; Converter: TGListFromStringConverter; SlicesCount: Integer = -1);
56 function Extract(Item: TGListItem): TGListItem;
57 property First: TGListItem read GetFirst write SetFirst;
58 procedure Fill(Start, Count: TGListIndex; Value: TGListItem);
59 function GetArray(Index, ACount: TGListIndex): TGListItemArray;
60 procedure GetList(List: TGList; Index, ACount: TGListIndex);
61 procedure GetBuffer(Index: TGListIndex; var Buffer; Count: TGListIndex);
62 function Implode(Separator: string; Converter: TGListToStringConverter): string;
63 function IndexOf(Item: TGListItem; Start: TGListIndex = 0): TGListIndex; virtual;
64 function IndexOfList(List: TGList; Start: TGListIndex = 0): TGListIndex;
65 function IndexOfArray(Values: array of TGListItem; Start: TGListIndex = 0): TGListIndex;
66 procedure Insert(Index: TGListIndex; Item: TGListItem);
67 procedure InsertList(Index: TGListIndex; List: TGList);
68 procedure InsertArray(Index: TGListIndex; Values: array of TGListItem);
69 procedure InsertCount(Index: TGListIndex; ACount: TGListIndex);
70 procedure Move(CurIndex, NewIndex: TGListIndex);
71 procedure MoveItems(CurIndex, NewIndex, Count: TGListIndex);
72 function Remove(Item: TGListItem): TGListIndex;
73 procedure Reverse;
74 procedure ReplaceArray(Index: TGListIndex; Values: array of TGListItem);
75 procedure ReplaceList(Index: TGListIndex; Source: TGList);
76 procedure ReplaceListPart(Index: TGListIndex; Source: TGList;
77 SourceIndex, SourceCount: TGListIndex);
78 procedure ReplaceBuffer(Index: TGListIndex; var Buffer; Count: TGListIndex);
79 procedure Sort(Compare: TGListSortCompare);
80 procedure SetArray(Values: array of TGListItem);
81 procedure BeginUpdate;
82 procedure EndUpdate;
83 procedure Update;
84 property Count: TGListIndex read FCount write SetCount;
85 property Capacity: TGListIndex read GetCapacity write SetCapacity;
86 property Items[Index: TGListIndex]: TGListItem read Get write Put; default;
87 property Last: TGListItem read GetLast write SetLast;
88 property OnUpdate: TNotifyEvent read FOnUpdate write FOnUpdate;
89 end;
90
91{$UNDEF INTERFACE}
92{$ENDIF}
93
94{$IFDEF IMPLEMENTATION_USES}
95
96uses
97 RtlConsts;
98
99{$UNDEF IMPLEMENTATION_USES}
100{$ENDIF}
101
102{$IFDEF IMPLEMENTATION}
103
104{ TGList }
105
106constructor TGList.Create;
107begin
108 FCount := 0;
109end;
110
111procedure TGList.GetBuffer(Index: TGListIndex; var Buffer; Count: TGListIndex);
112var
113 P: PItem;
114 I: TGListIndex;
115begin
116 if (Index + Count) > FCount then
117 raise EListError.CreateFmt(SListIndexError, [Index + Count]);
118 P := PItem(@Buffer);
119 I := 0;
120 while I < Count do begin
121 P^ := Items[Index + I];
122 Inc(P, 1);
123 I := I + 1;
124 end;
125end;
126
127procedure TGList.ReplaceBuffer(Index: TGListIndex; var Buffer; Count: TGListIndex);
128var
129 P: PItem;
130 I: TGListIndex;
131begin
132 if (Index + Count) > FCount then
133 raise EListError.CreateFmt(SListIndexError, [Index + Count]);
134 P := PItem(@Buffer);
135 I := 0;
136 while I < Count do begin
137 Items[Index + I] := P^;
138 Inc(P, 1);
139 I := I + 1;
140 end;
141end;
142
143procedure TGList.ReplaceArray(Index: TGListIndex; Values: array of TGListItem);
144var
145 I: TGListIndex;
146begin
147 I := 0;
148 while I < Length(Values) do begin
149 Items[Index + I] := Values[I];
150 I := I + 1;
151 end;
152 Update;
153end;
154
155procedure TGList.ReplaceList(Index: TGListIndex; Source: TGList);
156var
157 I: TGListIndex;
158begin
159 I := 0;
160 while I < Source.Count do begin
161 Items[Index + I] := Source[I];
162 I := I + 1;
163 end;
164 Update;
165end;
166
167procedure TGList.ReplaceListPart(Index: TGListIndex; Source: TGList;
168 SourceIndex, SourceCount: TGListIndex);
169var
170 I: TGListIndex;
171begin
172 I := 0;
173 while I < SourceCount do begin
174 Items[Index + I] := Source[SourceIndex + I];
175 I := I + 1;
176 end;
177 Update;
178end;
179
180function TGList.GetCapacity: TGListIndex;
181begin
182 Result := Length(FItems);
183end;
184
185procedure TGList.SetCapacity(const AValue: TGListIndex);
186begin
187 if (AValue < FCount) then
188 raise EListError.CreateFmt(SListCapacityError, [AValue]);
189 SetLength(FItems, AValue);
190end;
191
192procedure TGList.SetCapacityOptimized(const NewCapacity: TGListIndex);
193var
194 IncSize: TGListIndex;
195begin
196 if NewCapacity > Capacity then begin
197 IncSize := NewCapacity - Capacity;
198 // Expand
199 if IncSize = 1 then begin
200 IncSize := 4;
201 if Capacity > 3 then IncSize := IncSize + 4;
202 if Capacity > 8 then IncSize := IncSize + 8;
203 if Capacity > 63 then IncSize := IncSize + Capacity shr 2; // Grow by one quarter
204 end;
205 Capacity := Capacity + IncSize;
206 end else
207 if NewCapacity < Capacity then begin
208 // Contract
209 if (Capacity > 256) and (FCount < Capacity shr 2) then
210 begin
211 Capacity := Capacity shr 1;
212 end;
213 end;
214end;
215
216function TGList.Get(Index: TGListIndex): TGListItem;
217begin
218 if (Index < 0) or (Index >= Count) then
219 raise EListError.CreateFmt(SListIndexError, [Index]);
220 Result := FItems[Index];
221end;
222
223procedure TGList.Put(Index: TGListIndex; const AValue: TGListItem);
224begin
225 if (Index < 0) or (Index >= Count) then
226 raise EListError.CreateFmt(SListIndexError, [Index]);
227 FItems[Index] := AValue;
228end;
229
230procedure TGList.SetCount(const AValue: TGListIndex);
231begin
232 if (AValue < 0) then
233 raise EListError.CreateFmt(SListCountError, [AValue]);
234 if AValue > Capacity then SetCapacityOptimized(AValue); // Before FCount change
235 FCount := AValue;
236 if AValue < Capacity then SetCapacityOptimized(AValue); // After FCount change
237end;
238
239function TGList.GetArray(Index, ACount: TGListIndex): TGListItemArray;
240var
241 I: Integer;
242begin
243 SetLength(Result, ACount);
244 I := 0;
245 while I < Count do begin
246 Result[I] := FItems[Index + I];
247 I := I + 1;
248 end;
249end;
250
251procedure TGList.GetList(List: TGList; Index, ACount: TGListIndex);
252begin
253 List.Clear;
254 List.AddListPart(Self, Index, ACount);
255end;
256
257procedure TGList.QuickSort(L, R: TGListIndex; Compare: TGListSortCompare);
258var
259 I, J: TGListIndex;
260 P, Q: TGListItem;
261begin
262 repeat
263 I := L;
264 J := R;
265 P := FItems[(L + R) div 2];
266 repeat
267 while Compare(P, FItems[I]) > 0 do
268 I := I + 1;
269 while Compare(P, FItems[J]) < 0 do
270 J := J - 1;
271 if I <= J then
272 begin
273 Q := FItems[I];
274 FItems[I] := FItems[J];
275 FItems[J] := Q;
276 I := I + 1;
277 J := J - 1;
278 end;
279 until I > J;
280 if L < J then
281 QuickSort(L, J, Compare);
282 L := I;
283 until I >= R;
284end;
285
286procedure TGList.Assign(Source: TGList);
287var
288 I: TGListIndex;
289begin
290 Count := Source.Count;
291 I := 0;
292 while I < Count do begin
293 FItems[I] := Source[I];
294 I := I + 1;
295 end;
296 Update;
297end;
298
299function TGList.Extract(Item: TGListItem): TGListItem;
300var
301 I: TGListIndex;
302begin
303 I := IndexOf(Item);
304 if I >= 0 then begin
305 Result := Item;
306 Delete(I);
307 end else
308 raise EListError.CreateFmt(SListIndexError, [0]);
309end;
310
311function TGList.CompareMem(P1, P2: Pointer; Length: cardinal): Boolean;
312var
313 I: Cardinal;
314begin
315 Result := True;
316 I := 0;
317 if (P1) <> (P2) then
318 while Result and (I < Length) do
319 begin
320 Result := PByte(P1)^ = PByte(P2)^;
321 Inc(I);
322 Inc(pchar(P1));
323 Inc(pchar(P2));
324 end;
325end;
326
327function TGList.IndexOf(Item: TGListItem; Start: TGListIndex): TGListIndex;
328begin
329 Result := Start;
330 while (Result < FCount) and
331// not CompareMem(@FItems[Result], @Item, SizeOf(TGListItem)) do
332 not (CompareByte(FItems[Result], Item, SizeOf(TGListItem)) = 0) do
333 Result := Result + 1;
334 if Result = FCount then Result := -1;
335end;
336
337procedure TGList.Insert(Index: TGListIndex; Item: TGListItem);
338begin
339 if (Index < 0) or (Index > FCount) then
340 raise EListError.CreateFmt(SListIndexError, [Index]);
341 InsertCount(Index, 1);
342 FItems[Index] := Item;
343 Update;
344end;
345
346procedure TGList.InsertList(Index: TGListIndex; List: TGList);
347begin
348 if (Index < 0) or (Index > FCount) then
349 raise EListError.CreateFmt(SListIndexError, [Index]);
350 InsertCount(Index, List.Count);
351 ReplaceList(Index, List);
352end;
353
354procedure TGList.InsertArray(Index: TGListIndex; Values: array of TGListItem);
355begin
356 if (Index < 0) or (Index > FCount) then
357 raise EListError.CreateFmt(SListIndexError, [Index]);
358 InsertCount(Index, Length(Values));
359 ReplaceArray(Index, Values);
360end;
361
362procedure TGList.InsertCount(Index: TGListIndex; ACount: TGListIndex);
363begin
364 if (Index < 0) or (Index > FCount) then
365 raise EListError.CreateFmt(SListIndexError, [Index]);
366 Count := Count + ACount;
367 if Index < FCount then
368 System.Move(FItems[Index], FItems[Index + ACount], (FCount - ACount - Index) * SizeOf(TGListItem));
369 Update;
370end;
371
372function TGList.IndexOfList(List: TGList; Start: TGListIndex): TGListIndex;
373var
374 I: TGListIndex;
375begin
376 if List.Count > 0 then begin
377 Result := IndexOf(List[0], Start);
378 if Result <> -1 then begin
379 I := 1;
380 while I < List.Count do begin
381 if not CompareMem(Addr(FItems[Result + I]), Addr(List.FItems[I]), SizeOf(TGListItem)) then begin
382 Result := -1;
383 Break;
384 end;
385 I := I + 1;
386 end;
387 end;
388 end else Result := -1;
389end;
390
391function TGList.IndexOfArray(Values: array of TGListItem; Start: TGListIndex): TGListIndex;
392var
393 I: TGListIndex;
394begin
395 if Length(Values) > 0 then begin
396 Result := IndexOf(Values[0], Start);
397 if Result <> -1 then begin
398 I := 1;
399 while I < Length(Values) do begin
400 if not CompareMem(Addr(FItems[Result + I]), Addr(Values[I]), SizeOf(TGListItem)) then begin
401 Result := -1;
402 Break;
403 end;
404 I := I + 1;
405 end;
406 end;
407 end else Result := -1;
408end;
409
410function TGList.GetLast: TGListItem;
411begin
412 if FCount = 0 then
413 raise EListError.CreateFmt(SListIndexError, [0])
414 else
415 Result := FItems[FCount - 1];
416end;
417
418procedure TGList.SetLast(AValue: TGListItem);
419begin
420 if FCount = 0 then
421 raise EListError.CreateFmt(SListIndexError, [0])
422 else
423 FItems[FCount - 1] := AValue;
424end;
425
426function TGList.GetFirst: TGListItem;
427begin
428 if FCount = 0 then
429 raise EListError.CreateFmt(SListIndexError, [0])
430 else
431 Result := FItems[0];
432end;
433
434procedure TGList.SetFirst(AValue: TGListItem);
435begin
436 if FCount = 0 then
437 raise EListError.CreateFmt(SListIndexError, [0])
438 else
439 FItems[0] := AValue;
440end;
441
442procedure TGList.Move(CurIndex, NewIndex: TGListIndex);
443var
444 Temp: TGListItem;
445begin
446 if ((CurIndex < 0) or (CurIndex > Count - 1)) then
447 raise EListError.CreateFmt(SListIndexError, [CurIndex]);
448 if ((NewIndex < 0) or (NewIndex > Count -1)) then
449 raise EListError.CreateFmt(SlistIndexError, [NewIndex]);
450 Temp := FItems[CurIndex];
451 if NewIndex > CurIndex then begin
452 System.Move(FItems[CurIndex + 1], FItems[CurIndex], (NewIndex - CurIndex) * SizeOf(TGListItem));
453 end else
454 if NewIndex < CurIndex then begin
455 System.Move(FItems[NewIndex], FItems[NewIndex + 1], (CurIndex - NewIndex) * SizeOf(TGListItem));
456 end;
457 FItems[NewIndex] := Temp;
458 //Delete(CurIndex);
459 //Insert(NewIndex, Temp);
460 Update;
461end;
462
463procedure TGList.MoveItems(CurIndex, NewIndex, Count: TGListIndex);
464var
465 S: Integer;
466 D: Integer;
467begin
468 if CurIndex < NewIndex then begin
469 S := CurIndex + Count - 1;
470 D := NewIndex + Count - 1;
471 while S >= CurIndex do begin
472 Move(S, D);
473 S := S - 1;
474 D := D - 1;
475 end;
476 end else
477 if CurIndex > NewIndex then begin
478 S := CurIndex;
479 D := NewIndex;
480 while S < (CurIndex + Count) do begin
481 Move(S, D);
482 S := S + 1;
483 D := D + 1;
484 end;
485 end;
486 Update;
487end;
488
489function TGList.Remove(Item: TGListItem): TGListIndex;
490begin
491 Result := IndexOf(Item);
492 if Result <> -1 then
493 Delete(Result)
494 else raise Exception.CreateFmt(SItemNotFound, [0]);
495end;
496
497function TGList.EqualTo(List: TGList): Boolean;
498var
499 I: TGListIndex;
500begin
501 Result := Count = List.Count;
502 if Result then begin
503 I := 0;
504 while I < Count do begin
505 if not CompareMem(Addr(FItems[I]), Addr(List.FItems[I]), SizeOf(TGListItem)) then begin
506 Result := False;
507 Break;
508 end;
509 I := I + 1;
510 end;
511 end;
512end;
513
514procedure TGList.Reverse;
515var
516 I: TGListIndex;
517begin
518 I := 0;
519 while I < (Count div 2) do begin
520 Exchange(I, Count - 1 - I);
521 I := I + 1;
522 end;
523 Update;
524end;
525
526procedure TGList.Sort(Compare: TGListSortCompare);
527begin
528 if FCount > 1 then
529 QuickSort(0, FCount - 1, Compare);
530 Update;
531end;
532
533procedure TGList.AddArray(Values: array of TGListItem);
534var
535 I: TGListIndex;
536begin
537 I := 0;
538 while I <= High(Values) do begin
539 Add(Values[I]);
540 I := I + 1;
541 end;
542 Update;
543end;
544
545procedure TGList.SetArray(Values: array of TGListItem);
546var
547 I: TGListIndex;
548begin
549 Clear;
550 I := 0;
551 while I <= High(Values) do begin
552 Add(Values[I]);
553 I := I + 1;
554 end;
555end;
556
557procedure TGList.BeginUpdate;
558begin
559 Inc(FUpdateCount);
560end;
561
562procedure TGList.EndUpdate;
563begin
564 Dec(FUpdateCount);
565 Update;
566end;
567
568procedure TGList.Update;
569begin
570 if Assigned(FOnUpdate) and (FUpdateCount = 0) then FOnUpdate(Self);
571end;
572
573function TGList.Implode(Separator: string; Converter: TGListToStringConverter): string;
574var
575 I: TGListIndex;
576begin
577 Result := '';
578 I := 0;
579 while I < Count do begin
580 Result := Result + Converter(FItems[I]);
581 if I < (Count - 1) then
582 Result := Result + Separator;
583 I := I + 1;
584 end;
585end;
586
587procedure TGList.Explode(Text, Separator: string; Converter: TGListFromStringConverter; SlicesCount: Integer = -1);
588begin
589 Clear;
590 while (Pos(Separator, Text) > 0) and
591 ((Count < (SlicesCount - 1)) or (SlicesCount = -1)) do begin
592 Add(Converter(Copy(Text, 1, Pos(Separator, Text) - 1)));
593 System.Delete(Text, 1, Pos(Separator, Text) + Length(Separator) - 1);
594 end;
595 Add(Converter(Text));
596end;
597
598function TGList.Add(Item: TGListItem): TGListIndex;
599begin
600 Count := Count + 1;
601 Result := FCount - 1;
602 FItems[Result] := Item;
603 Update;
604end;
605
606procedure TGList.AddList(List: TGList);
607var
608 I: TGListIndex;
609 J: TGListIndex;
610begin
611 I := Count;
612 J := 0;
613 Count := Count + List.Count;
614 while I < Count do begin
615 Items[I] := List[J];
616 I := I + 1;
617 J := J + 1;
618 end;
619 Update;
620end;
621
622procedure TGList.AddListPart(List: TGList; ItemIndex, ItemCount: TGListIndex);
623var
624 I: TGListIndex;
625 J: TGListIndex;
626begin
627 I := Count;
628 J := ItemIndex;
629 Count := Count + ItemCount;
630 while I < Count do begin
631 Items[I] := List[J];
632 I := I + 1;
633 J := J + 1;
634 end;
635 Update;
636end;
637
638procedure TGList.Clear;
639begin
640 Count := 0;
641 Capacity := 0;
642end;
643
644procedure TGList.Delete(Index: TGListIndex);
645begin
646 if (Index < 0) or (Index >= FCount) then
647 raise EListError.CreateFmt(SListIndexError, [Index]);
648 FCount := FCount - 1;
649 System.Move(FItems[Index + 1], FItems[Index], (FCount - Index) * SizeOf(TGListItem));
650 SetCapacityOptimized(Capacity - 1);
651 Update;
652end;
653
654procedure TGList.DeleteItems(Index, Count: TGListIndex);
655var
656 I: TGListIndex;
657begin
658 I := Index;
659 while I < (Index + Count) do begin
660 Delete(Index);
661 I := I + 1;
662 end;
663 Update;
664end;
665
666procedure TGList.Fill(Start, Count: TGListIndex; Value: TGListItem);
667var
668 I: TGListIndex;
669begin
670 I := Start;
671 while I < Count do begin
672 FItems[I] := Value;
673 I := I + 1;
674 end;
675 Update;
676end;
677
678procedure TGList.Exchange(Index1, Index2: TGListIndex);
679var
680 Temp: TGListItem;
681begin
682 if ((Index1 >= FCount) or (Index1 < 0)) then
683 raise EListError.CreateFmt(SListIndexError, [Index1]);
684 if ((Index2 >= FCount) or (Index2 < 0)) then
685 raise EListError.CreateFmt(SListIndexError, [Index2]);
686 Temp := FItems[Index1];
687 FItems[Index1] := FItems[Index2];
688 FItems[Index2] := Temp;
689 Update;
690end;
691
692{$UNDEF IMPLEMENTATION}
693{$ENDIF}
Note: See TracBrowser for help on using the repository browser.