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

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