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

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