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

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