This repository has been archived by the owner on Nov 27, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4
/
EvilWorks.Generics.List.pas
354 lines (312 loc) · 8.38 KB
/
EvilWorks.Generics.List.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
//
// EvilLibrary by Vedran Vuk 2010-2012
//
// Name: EvilWorks.DataStructures.AVLTree
// Description: A Generic list implementation.
// File last change date: November 17th. 2012
// File version: Dev 0.0.0
// Licence: Free.
//
unit EvilWorks.Generics.List;
interface
uses
System.SysUtils;
type
EList = class(Exception);
EListIndexOutOfBounds = class(EList);
{ TList<T> }
{ A Generic list. }
TList<T> = class
public type
TCreateFunc = reference to function: T;
TDestroyProc = reference to procedure(var aItem: T);
TAssignProc = reference to procedure(const aFromItem: T; var aToItem: T);
TCompareFunc = reference to function(const aItemA, aItemB: T): integer;
private type
{ TListEnumerator }
TListEnumerator = class
private
FIndex : integer;
FList: TList<T>;
public
constructor Create(aList: TList<T>);
function GetCurrent: T; inline;
function MoveNext: Boolean; inline;
property Current: T read GetCurrent;
end;
private
FItems : array of T;
FCount : integer;
FSorted : boolean;
FCreate : TCreateFunc;
FCompare: TCompareFunc;
FAssign : TAssignProc;
FDestroy: TDestroyProc;
function GetT(const aIndex: integer): T;
procedure SetT(const aIndex: integer; const Value: T);
protected
procedure QuickSort(const aStart, aEnd: integer);
public
constructor Create(const aCreate: TCreateFunc; const aDestroy: TDestroyProc; const aAssign: TAssignProc; const aCompare: TCompareFunc);
destructor Destroy; override;
procedure Assign(const aSource: TList<T>);
function GetEnumerator: TListEnumerator;
function Add: T; overload;
function Add(const aItem: T): T; overload;
function AddSorted: T; overload;
function AddSorted(const aItem: T): T; overload;
function Insert(const aIndex: integer): T; overload;
function Insert(const aIndex: integer; const aItem: T): T; overload;
procedure Exchange(const aIndexA, aIndexB: integer);
procedure Delete(const aIndex: integer);
procedure Clear;
procedure Sort;
function IndexOf(const aVal: T): integer;
property Items[const aIndex: integer]: T read GetT write SetT; default;
property Count: integer read FCount;
property Sorted: boolean read FSorted;
end;
implementation
{ ======================== }
{ TList<T>.TListEnumerator }
{ ======================== }
{ Constructor. }
constructor TList<T>.TListEnumerator.Create(aList: TList<T>);
begin
inherited Create;
FIndex := - 1;
FList := aList;
end;
{ Gets curent item for the iterator. }
function TList<T>.TListEnumerator.GetCurrent: T;
begin
Result := FList[FIndex];
end;
{ Advances to next item for the iterator. }
function TList<T>.TListEnumerator.MoveNext: Boolean;
begin
Result := (FIndex < FList.Count - 1);
if Result then
Inc(FIndex);
end;
{ ======== }
{ TList<T> }
{ ======== }
{ Constructor. }
constructor TList<T>.Create(const aCreate: TCreateFunc; const aDestroy: TDestroyProc; const aAssign: TAssignProc; const aCompare: TCompareFunc);
begin
FCount := 0;
FSorted := False;
FCreate := aCreate;
FDestroy := aDestroy;
FAssign := aAssign;
FCompare := aCompare;
end;
{ Destructor. }
destructor TList<T>.Destroy;
begin
Clear;
inherited;
end;
{ Assign from an instance of the same type. }
procedure TList<T>.Assign(const aSource: TList<T>);
var
i: integer;
c: T;
begin
Clear;
for i := 0 to aSource.Count - 1 do
begin
c := Add;
FAssign(aSource[i], c);
end;
end;
{ Implements GetEnumerator. }
function TList<T>.GetEnumerator: TListEnumerator;
begin
Result := TListEnumerator.Create(Self);
end;
{ Add a new item to the list. Uses aCreate function from constructor to create a new item. }
function TList<T>.Add: T;
begin
Result := Add(FCreate);
end;
{ Add aItem to the list. }
function TList<T>.Add(const aItem: T): T;
begin
Result := Insert(FCount, aItem);
end;
{ Add a new item to the list, sort if not already sorted. Adding is done using partitioning, fast. }
{ Uses aCreate function from constructor to create a new item. }
function TList<T>.AddSorted: T;
begin
Result := AddSorted(FCreate);
end;
{ Add aItem to the list, sort if not already sorted. Adding is done using partitioning, fast. }
function TList<T>.AddSorted(const aItem: T): T;
var
loIdx, hiIdx, i: integer;
begin
Sort;
if (FCount <> 0) then
begin
loIdx := 0;
hiIdx := FCount;
while (loIdx < hiIdx) do
begin
i := ((loIdx + hiIdx) shr 1);
if (FCompare(aItem, FItems[i]) = - 1) then
hiIdx := i
else
loIdx := i + 1;
end;
i := loIdx;
end
else
i := 0;
Insert(i, aItem);
// Insert unmarks FSorted, but the insert index is found using
// bisection and is 'sorted', so just re-mark as sorted.
FSorted := True;
end;
{ Add a new item to the list at aIndex. Uses aCreate function from constructor to create a new item. }
{ If the list is sorted, Sorted state is broken and needs to be sorted again. }
function TList<T>.Insert(const aIndex: integer): T;
begin
Result := Insert(FCount, FCreate);
end;
{ Add aItem to the list at aIndex. }
{ If the list was sorted, Sorted state is broken and needs to be sorted again. }
function TList<T>.Insert(const aIndex: integer; const aItem: T): T;
begin
if (aIndex < 0) or (aIndex > FCount) then
raise EArgumentOutOfRangeException.Create(Format('Index %d out of bounds %d.', [aIndex, FCount]));
SetLength(FItems, FCount + 1);
if (aIndex < FCount) then
System.Move(FItems[aIndex], FItems[aIndex + 1], (FCount - aIndex) * SizeOf(T));
FItems[aIndex] := aItem;
Inc(FCount);
FSorted := False;
end;
{ Exchange position of items at aIndexA and aIndexB. }
{ If the list was sorted, Sorted state is broken and needs to be sorted again. }
procedure TList<T>.Exchange(const aIndexA, aIndexB: integer);
var
temp: T;
begin
temp := FItems[aIndexB];
FItems[aIndexB] := FItems[aIndexA];
FItems[aIndexA] := temp;
FSorted := False;
end;
{ Delete an item from the list at aIndex. Uses aDestroy from constructor to free the item. }
procedure TList<T>.Delete(const aIndex: integer);
begin
if (aIndex < 0) or (aIndex > FCount) then
raise EArgumentOutOfRangeException.Create(Format('Index %d out of bounds %d.', [aIndex, FCount]));
FDestroy(FItems[aIndex]);
Dec(FCount);
if (aIndex < FCount) then
System.Move(FItems[aIndex + 1], FItems[aIndex], (FCount - aIndex) * SizeOf(T));
end;
{ Clear the list. Uses aDestroy from constructor to free each item. }
procedure TList<T>.Clear;
var
i: integer;
begin
for i := 0 to FCount - 1 do
FDestroy(FItems[i]);
SetLength(FItems, 0);
FCount := 0;
end;
{ Internal QuickSort function. Uses aCompare function from constructor to compare items when sorting. }
procedure TList<T>.QuickSort(const aStart, aEnd: integer);
var
a: Integer;
i: Integer;
j: Integer;
p: Integer;
begin
if (FCount <= 1) then
Exit;
a := aStart;
repeat
i := a;
j := aEnd;
p := (a + aEnd) shr 1;
repeat
while (FCompare(FItems[i], FItems[p]) < 0) do
Inc(i);
while (FCompare(FItems[j], FItems[p]) > 0) do
Dec(j);
if (i <= j) then
begin
if (i <> j) then
Exchange(i, j);
if (p = i) then
p := j
else if (p = j) then
p := i;
Inc(i);
Dec(j);
end;
until (i > j);
if (a < j) then
QuickSort(a, j);
a := i;
until (i >= aEnd);
end;
{ Sort the list. }
procedure TList<T>.Sort;
begin
if (FSorted) then
Exit;
QuickSort(0, FCount - 1);
FSorted := True;
end;
{ Find the index of aVal. }
function TList<T>.IndexOf(const aVal: T): integer;
var
loIdx, hiIdx, cnt, i: integer;
begin
Result := - 1;
if (FSorted) then
begin
loIdx := 0;
hiIdx := (FCount - 1);
while (loIdx <= hiIdx) do
begin
cnt := ((loIdx + hiIdx) shr 1);
i := FCompare(FItems[cnt], aVal);
if (i < 0) then
loIdx := (cnt + 1)
else
begin
hiIdx := (cnt - 1);
if (i = 0) then
Exit(loIdx);
end;
end;
end
else
begin
for i := 0 to FCount - 1 do
if (FCompare(FItems[i], aVal) = 0) then
Exit(i);
end;
end;
{ Items getter. }
function TList<T>.GetT(const aIndex: integer): T;
begin
if (aIndex < 0) or (aIndex >= FCount) then
raise EListIndexOutOfBounds.Create(Format('Index %d out of bounds %d.', [aIndex, FCount]));
Result := FItems[aIndex];
end;
{ Items setter. }
procedure TList<T>.SetT(const aIndex: integer; const Value: T);
begin
if (aIndex < 0) or (aIndex >= FCount) then
raise EListIndexOutOfBounds.Create(Format('Index %d out of bounds %d.', [aIndex, FCount]));
FAssign(Value, FItems[aIndex]);
end;
end.