source: DelphiTemplates/Generic/GenericList.inc

Last change on this file was 2, checked in by chronos, 13 years ago
File size: 13.4 KB
Line 
1{$IFDEF INTERFACE}
2
3 TGList = class;
4
5
6 // TGList<TGListIndex, TGListItem> = class
7 TGList = class
8 type
9 TSortCompare = function(Item1, Item2: TGListItem): Integer of object;
10 TToStringConverter = function(Item: TGListItem): string;
11 TFromStringConverter = function(Text: string): TGListItem;
12 TItemArray = array of TGListItem;
13 private
14 FItems: array of TGListItem;
15 FCount: TGListIndex;
16 function Get(Index: TGListIndex): TGListItem;
17 function GetCapacity: TGListIndex;
18 function GetLast: TGListItem;
19 function GetFirst: TGListItem;
20 procedure SetCapacity(const AValue: TGListIndex);
21 procedure SetCapacityOptimized(const NewCapacity: TGListIndex);
22 procedure SetLast(AValue: TGListItem);
23 procedure SetFirst(AValue: TGListItem);
24 procedure Put(Index: TGListIndex; const AValue: TGListItem); virtual;
25 procedure SetCount(const AValue: TGListIndex); virtual;
26 procedure QuickSort(L, R : TGListIndex; Compare: TSortCompare);
27 public
28 function CompareMem(P1, P2: Pointer; Length: cardinal): Boolean; inline;
29 function Add(Item: TGListItem): TGListIndex;
30 procedure AddArray(Values: array of TGListItem);
31 procedure AddList(List: TGList);
32 procedure Assign(Source: TGList); virtual;
33 procedure Clear; virtual;
34 procedure Delete(Index: TGListIndex); virtual;
35 procedure DeleteItems(Index, Count: TGListIndex);
36 function EqualTo(List: TGList): Boolean;
37 procedure Exchange(Index1, Index2: TGListIndex);
38 procedure Explode(Text, Separator: string; Converter: TFromStringConverter; SlicesCount: Integer = -1);
39 function Extract(Item: TGListItem): TGListItem;
40 property First: TGListItem read GetFirst write SetFirst;
41 procedure Fill(Start, Count: TGListIndex; Value: TGListItem);
42 function GetArray: TItemArray;
43 function Implode(Separator: string; Converter: TToStringConverter): string;
44 function IndexOf(Item: TGListItem; Start: TGListIndex = 0): TGListIndex;
45 function IndexOfList(List: TGList; Start: TGListIndex = 0): TGListIndex;
46 procedure Insert(Index: TGListIndex; Item: TGListItem);
47 procedure InsertList(Index: TGListIndex; List: TGList);
48 procedure InsertArray(Index: TGListIndex; Values: array of TGListItem);
49 procedure Move(CurIndex, NewIndex: TGListIndex);
50 procedure MoveItems(CurIndex, NewIndex, Count: TGListIndex);
51 function Remove(Item: TGListItem): TGListIndex;
52 procedure Reverse;
53 procedure Replace(Index: TGListIndex; Source: TGList);
54 procedure Sort(Compare: TSortCompare);
55 procedure SetArray(Values: TItemArray);
56 property Count: TGListIndex read FCount write SetCount;
57 property Capacity: TGListIndex read GetCapacity write SetCapacity;
58 property Items[Index: TGListIndex]: TGListItem read Get write Put; default;
59 property Last: TGListItem read GetLast write SetLast;
60 end;
61
62{$UNDEF INTERFACE}
63{$ENDIF}
64
65{$IFDEF IMPLEMENTATION_USES}
66
67uses
68 RtlConsts;
69
70{$UNDEF IMPLEMENTATION_USES}
71{$ENDIF}
72
73{$IFDEF IMPLEMENTATION}
74
75{ TGList }
76
77procedure TGList.Replace(Index: TGListIndex; Source: TGList);
78var
79 I: TGListIndex;
80begin
81 I := 0;
82 while I < Source.Count do begin
83 Items[Index + I] := Source[I];
84 I := I + 1;
85 end;
86end;
87
88function TGList.GetCapacity: TGListIndex;
89begin
90 Result := Length(FItems);
91end;
92
93procedure TGList.SetCapacity(const AValue: TGListIndex);
94begin
95 if (AValue < FCount) then
96 raise EListError.CreateFmt(SListCapacityError, [AValue]);
97 SetLength(FItems, AValue);
98end;
99
100procedure TGList.SetCapacityOptimized(const NewCapacity: TGListIndex);
101var
102 IncSize: TGListIndex;
103begin
104 if NewCapacity > Capacity then begin
105 IncSize := NewCapacity - Capacity;
106 // Expand
107 if IncSize = 1 then begin
108 IncSize := 4;
109 if Capacity > 3 then IncSize := IncSize + 4;
110 if Capacity > 8 then IncSize := IncSize + 8;
111 if Capacity > 63 then IncSize := IncSize + Capacity shr 2; // Grow by one quarter
112 end;
113 Capacity := Capacity + IncSize;
114 end else
115 if NewCapacity < Capacity then begin
116 // Contract
117 if (Capacity > 256) and (FCount < Capacity shr 2) then
118 begin
119 Capacity := Capacity shr 1;
120 end;
121 end;
122end;
123
124function TGList.Get(Index: TGListIndex): TGListItem;
125begin
126 if (Index < 0) or (Index >= Count) then
127 raise EListError.CreateFmt(SListIndexError, [Index]);
128 Result := FItems[Index];
129end;
130
131procedure TGList.Put(Index: TGListIndex; const AValue: TGListItem);
132begin
133 if (Index < 0) or (Index >= Count) then
134 raise EListError.CreateFmt(SListIndexError, [Index]);
135 FItems[Index] := AValue;
136end;
137
138procedure TGList.SetCount(const AValue: TGListIndex);
139begin
140 if (AValue < 0) then
141 raise EListError.CreateFmt(SListCountError, [AValue]);
142 if AValue > Capacity then SetCapacityOptimized(AValue); // Before FCount change
143 FCount := AValue;
144 if AValue < Capacity then SetCapacityOptimized(AValue); // After FCount change
145end;
146
147function TGList.GetArray: TItemArray;
148var
149 I: Integer;
150begin
151 SetLength(Result, Count);
152 I := 0;
153 while I < Count do begin
154 Result[I] := FItems[I];
155 I := I + 1;
156 end;
157end;
158
159procedure TGList.QuickSort(L, R: TGListIndex; Compare: TSortCompare);
160var
161 I, J: TGListIndex;
162 P, Q: TGListItem;
163begin
164 repeat
165 I := L;
166 J := R;
167 P := FItems[(L + R) div 2];
168 repeat
169 while Compare(P, FItems[I]) > 0 do
170 I := I + 1;
171 while Compare(P, FItems[J]) < 0 do
172 J := J - 1;
173 if I <= J then
174 begin
175 Q := FItems[I];
176 FItems[I] := FItems[J];
177 FItems[J] := Q;
178 I := I + 1;
179 J := J - 1;
180 end;
181 until I > J;
182 if L < J then
183 QuickSort(L, J, Compare);
184 L := I;
185 until I >= R;
186end;
187
188procedure TGList.Assign(Source: TGList);
189var
190 I: TGListIndex;
191begin
192 Count := Source.Count;
193 I := 0;
194 while I < Count do begin
195 FItems[I] := Source[I];
196 I := I + 1;
197 end;
198end;
199
200function TGList.Extract(Item: TGListItem): TGListItem;
201var
202 I: TGListIndex;
203begin
204 I := IndexOf(Item);
205 if I >= 0 then begin
206 Result := Item;
207 Delete(I);
208 end else
209 raise EListError.CreateFmt(SListIndexError, [0]);
210end;
211
212function TGList.CompareMem(P1, P2: Pointer; Length: cardinal): Boolean;
213var
214 I: Cardinal;
215begin
216 Result := True;
217 I := 0;
218 if (P1) <> (P2) then
219 while Result and (I < Length) do
220 begin
221 Result := PByte(P1)^ = PByte(P2)^;
222 Inc(I);
223 Inc(pchar(P1));
224 Inc(pchar(P2));
225 end;
226end;
227
228function TGList.IndexOf(Item: TGListItem; Start: TGListIndex): TGListIndex;
229begin
230 Result := Start;
231 while (Result < FCount) and
232 not CompareMem(@FItems[Result], @Item, SizeOf(TGListItem)) do
233// not (CompareByte(FItems[Result], Item, SizeOf(TGListItem)) = 0) do
234 Result := Result + 1;
235 if Result = FCount then Result := -1;
236end;
237
238procedure TGList.Insert(Index: TGListIndex; Item: TGListItem);
239begin
240 if (Index < 0) or (Index > FCount ) then
241 raise EListError.CreateFmt(SListIndexError, [Index]);
242 if FCount = Capacity then SetCapacityOptimized(Capacity + 1);
243 if Index < FCount then
244 System.Move(FItems[Index], FItems[Index + 1], (FCount - Index) * SizeOf(TGListItem));
245 FItems[Index] := Item;
246 FCount := FCount + 1;
247end;
248
249procedure TGList.InsertList(Index: TGListIndex; List: TGList);
250var
251 I: TGListIndex;
252begin
253 I := 0;
254 while (I < List.Count) do begin
255 Insert(Index + I, List[I]);
256 I := I + 1;
257 end;
258end;
259
260function TGList.IndexOfList(List: TGList; Start: TGListIndex): TGListIndex;
261var
262 I: TGListIndex;
263begin
264 if List.Count > 0 then begin
265 Result := IndexOf(List[0], Start);
266 if Result <> -1 then begin
267 I := 1;
268 while I < List.Count do begin
269 if not CompareMem(Addr(FItems[Result + I]), Addr(List.FItems[I]), SizeOf(TGListItem)) then begin
270 Result := -1;
271 Break;
272 end;
273 I := I + 1;
274 end;
275 end;
276 end else Result := -1;
277end;
278
279function TGList.GetLast: TGListItem;
280begin
281 if FCount = 0 then
282 raise EListError.CreateFmt(SListIndexError, [0])
283 else
284 Result := FItems[FCount - 1];
285end;
286
287procedure TGList.SetLast(AValue: TGListItem);
288begin
289 if FCount = 0 then
290 raise EListError.CreateFmt(SListIndexError, [0])
291 else
292 FItems[FCount - 1] := AValue;
293end;
294
295function TGList.GetFirst: TGListItem;
296begin
297 if FCount = 0 then
298 raise EListError.CreateFmt(SListIndexError, [0])
299 else
300 Result := FItems[0];
301end;
302
303procedure TGList.SetFirst(AValue: TGListItem);
304begin
305 if FCount = 0 then
306 raise EListError.CreateFmt(SListIndexError, [0])
307 else
308 FItems[0] := AValue;
309end;
310
311procedure TGList.Move(CurIndex, NewIndex: TGListIndex);
312var
313 Temp: TGListItem;
314begin
315 if ((CurIndex < 0) or (CurIndex > Count - 1)) then
316 raise EListError.CreateFmt(SListIndexError, [CurIndex]);
317 if ((NewIndex < 0) or (NewIndex > Count -1)) then
318 raise EListError.CreateFmt(SlistIndexError, [NewIndex]);
319 Temp := FItems[CurIndex];
320 if NewIndex > CurIndex then begin
321 System.Move(FItems[CurIndex + 1], FItems[CurIndex], (NewIndex - CurIndex) * SizeOf(TGListItem));
322 end else
323 if NewIndex < CurIndex then begin
324 System.Move(FItems[NewIndex], FItems[NewIndex + 1], (CurIndex - NewIndex) * SizeOf(TGListItem));
325 end;
326 FItems[NewIndex] := Temp;
327 //Delete(CurIndex);
328 //Insert(NewIndex, Temp);
329end;
330
331procedure TGList.MoveItems(CurIndex, NewIndex, Count: TGListIndex);
332var
333 S: Integer;
334 D: Integer;
335begin
336 if CurIndex < NewIndex then begin
337 S := CurIndex + Count - 1;
338 D := NewIndex + Count - 1;
339 while S >= CurIndex do begin
340 Move(S, D);
341 S := S - 1;
342 D := D - 1;
343 end;
344 end else
345 if CurIndex > NewIndex then begin
346 S := CurIndex;
347 D := NewIndex;
348 while S < (CurIndex + Count) do begin
349 Move(S, D);
350 S := S + 1;
351 D := D + 1;
352 end;
353 end;
354end;
355
356function TGList.Remove(Item: TGListItem): TGListIndex;
357begin
358 Result := IndexOf(Item);
359 if Result <> -1 then
360 Delete(Result);
361end;
362
363function TGList.EqualTo(List: TGList): Boolean;
364var
365 I: TGListIndex;
366begin
367 Result := Count = List.Count;
368 if Result then begin
369 I := 0;
370 while I < Count do begin
371 if not CompareMem(Addr(FItems[I]), Addr(List.FItems[I]), SizeOf(TGListItem)) then begin
372 Result := False;
373 Break;
374 end;
375 I := I + 1;
376 end;
377 end;
378end;
379
380procedure TGList.Reverse;
381var
382 I: TGListIndex;
383begin
384 I := 0;
385 while I < (Count div 2) do begin
386 Exchange(I, Count - 1 - I);
387 I := I + 1;
388 end;
389end;
390
391procedure TGList.Sort(Compare: TSortCompare);
392begin
393 if FCount > 1 then
394 QuickSort(0, FCount - 1, Compare);
395end;
396
397procedure TGList.AddArray(Values: array of TGListItem);
398var
399 I: TGListIndex;
400begin
401 I := 0;
402 while I <= High(Values) do begin
403 Add(Values[I]);
404 I := I + 1;
405 end;
406end;
407
408procedure TGList.SetArray(Values: TItemArray);
409var
410 I: TGListIndex;
411begin
412 Clear;
413 I := 0;
414 while I <= High(Values) do begin
415 Add(Values[I]);
416 I := I + 1;
417 end;
418end;
419
420procedure TGList.InsertArray(Index: TGListIndex; Values: array of TGListItem);
421var
422 I: TGListIndex;
423begin
424 I := 0;
425 while I <= High(Values) do begin
426 Insert(Index + I, Values[I]);
427 I := I + 1;
428 end;
429end;
430
431function TGList.Implode(Separator: string; Converter: TToStringConverter): string;
432var
433 I: TGListIndex;
434begin
435 Result := '';
436 I := 0;
437 while I < Count do begin
438 Result := Result + Converter(FItems[I]);
439 if I < (Count - 1) then
440 Result := Result + Separator;
441 I := I + 1;
442 end;
443end;
444
445procedure TGList.Explode(Text, Separator: string; Converter: TFromStringConverter; SlicesCount: Integer = -1);
446begin
447 Clear;
448 while (Pos(Separator, Text) > 0) and
449 ((Count < (SlicesCount - 1)) or (SlicesCount = -1)) do begin
450 Add(Converter(Copy(Text, 1, Pos(Separator, Text) - 1)));
451 System.Delete(Text, 1, Pos(Separator, Text) + Length(Separator) - 1);
452 end;
453 Add(Converter(Text));
454end;
455
456function TGList.Add(Item: TGListItem): TGListIndex;
457begin
458 Count := Count + 1;
459 Result := FCount - 1;
460 FItems[Result] := Item;
461end;
462
463procedure TGList.AddList(List: TGList);
464var
465 I: TGListIndex;
466begin
467 I := 0;
468 while I < List.Count do begin
469 Add(List[I]);
470 I := I + 1;
471 end;
472end;
473
474procedure TGList.Clear;
475begin
476 Count := 0;
477 Capacity := 0;
478end;
479
480procedure TGList.Delete(Index: TGListIndex);
481begin
482 if (Index < 0) or (Index >= FCount) then
483 raise EListError.CreateFmt(SListIndexError, [Index]);
484 FCount := FCount - 1;
485 System.Move(FItems[Index + 1], FItems[Index], (FCount - Index) * SizeOf(TGListItem));
486 SetCapacityOptimized(Capacity - 1);
487end;
488
489procedure TGList.DeleteItems(Index, Count: TGListIndex);
490var
491 I: TGListIndex;
492begin
493 I := Index;
494 while I < (Index + Count) do begin
495 Delete(Index);
496 I := I + 1;
497 end;
498end;
499
500procedure TGList.Fill(Start, Count: TGListIndex; Value: TGListItem);
501var
502 I: TGListIndex;
503begin
504 I := Start;
505 while I < Count do begin
506 FItems[I] := Value;
507 I := I + 1;
508 end;
509end;
510
511procedure TGList.Exchange(Index1, Index2: TGListIndex);
512var
513 Temp: TGListItem;
514begin
515 if ((Index1 >= FCount) or (Index1 < 0)) then
516 raise EListError.CreateFmt(SListIndexError, [Index1]);
517 if ((Index2 >= FCount) or (Index2 < 0)) then
518 raise EListError.CreateFmt(SListIndexError, [Index2]);
519 Temp := FItems[Index1];
520 FItems[Index1] := FItems[Index2];
521 FItems[Index2] := Temp;
522end;
523
524{$UNDEF IMPLEMENTATION}
525{$ENDIF}
Note: See TracBrowser for help on using the repository browser.