fgl.pp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2006 by Florian Klaempfl
  4. It contains the Free Pascal generics library
  5. member of the Free Pascal development team
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. {$mode objfpc}
  13. { be aware, this unit is a prototype and subject to be changed heavily }
  14. unit fgl;
  15. interface
  16. const
  17. MaxListSize = Maxint div 16;
  18. type
  19. { TFPList class }
  20. generic TGList<TG> = class(TObject)
  21. type
  22. PTGList = ^TTGList;
  23. TTGList = array[0..MaxListSize - 1] of TG;
  24. TListSortCompare = function (Item1, Item2: TG): Integer;
  25. TListCallback = procedure(data,arg: TG) of object;
  26. TListStaticCallback = procedure(data,arg: TG);
  27. var
  28. private
  29. FList: PTGList;
  30. FCount: Integer;
  31. FCapacity: Integer;
  32. protected
  33. function Get(Index: Integer): TG; inline;
  34. procedure Put(Index: Integer; Item: TG); inline;
  35. procedure SetCapacity(NewCapacity: Integer);
  36. procedure SetCount(NewCount: Integer);
  37. Procedure RaiseIndexError(Index : Integer);
  38. public
  39. destructor Destroy; override;
  40. function Add(const Item: TG): Integer; inline;
  41. procedure Clear;
  42. procedure Delete(Index: Integer); inline;
  43. class procedure Error(const Msg: string; Data: PtrInt);
  44. procedure Exchange(Index1, Index2: Integer);
  45. function Expand: TGList; inline;
  46. function Extract(const item: TG): TG;
  47. function First: TG;
  48. function IndexOf(const Item: TG): Integer;
  49. procedure Insert(Index: Integer; Item: TG); inline;
  50. function Last: TG;
  51. procedure Move(CurIndex, NewIndex: Integer);
  52. procedure Assign(Obj:TGList);
  53. function Remove(const Item: TG): Integer;
  54. procedure Pack;
  55. procedure Sort(Compare: TListSortCompare);
  56. procedure ForEachCall(proc2call:TListCallback;arg:pointer);
  57. procedure ForEachCall(proc2call:TListStaticCallback;arg:pointer);
  58. property Capacity: Integer read FCapacity write SetCapacity;
  59. property Count: Integer read FCount write SetCount;
  60. property Items[Index: Integer]: TG read Get write Put; default;
  61. property List: PTGList read FList;
  62. end;
  63. implementation
  64. uses
  65. rtlconsts,sysutils,classes;
  66. {****************************************************************************}
  67. {* TGList *}
  68. {****************************************************************************}
  69. procedure TGList.RaiseIndexError(Index : Integer);
  70. begin
  71. Error(SListIndexError, Index);
  72. end;
  73. function TGList.Get(Index: Integer): Pointer; inline;
  74. begin
  75. If (Index < 0) or (Index >= FCount) then
  76. RaiseIndexError(Index);
  77. Result:=FList^[Index];
  78. end;
  79. procedure TGList.Put(Index: Integer; Item: Pointer); inline;
  80. begin
  81. if (Index < 0) or (Index >= FCount) then
  82. RaiseIndexError(Index);
  83. Flist^[Index] := Item;
  84. end;
  85. function TGList.Extract(const item: TG): TG;
  86. var
  87. i : Integer;
  88. begin
  89. result := nil;
  90. i := IndexOf(item);
  91. if i >= 0 then
  92. begin
  93. Result := item;
  94. FList^[i] := nil;
  95. Delete(i);
  96. end;
  97. end;
  98. procedure TGList.SetCapacity(NewCapacity: Integer);
  99. begin
  100. If (NewCapacity < FCount) or (NewCapacity > MaxListSize) then
  101. Error (SListCapacityError, NewCapacity);
  102. if NewCapacity = FCapacity then
  103. exit;
  104. ReallocMem(FList, SizeOf(Pointer)*NewCapacity);
  105. FCapacity := NewCapacity;
  106. end;
  107. procedure TGList.SetCount(NewCount: Integer);
  108. Const
  109. // Ratio of Pointer and Word Size.
  110. WordRatio = SizeOf(TG) Div SizeOf(Word);
  111. begin
  112. if (NewCount < 0) or (NewCount > MaxListSize)then
  113. Error(SListCountError, NewCount);
  114. If NewCount > FCount then
  115. begin
  116. If NewCount > FCapacity then
  117. SetCapacity(NewCount);
  118. If FCount < NewCount then
  119. FillWord(Flist^[FCount], (NewCount-FCount) * WordRatio, 0);
  120. end;
  121. FCount := Newcount;
  122. end;
  123. destructor TGList.Destroy;
  124. begin
  125. Self.Clear;
  126. inherited Destroy;
  127. end;
  128. function TGList.Add(const Item: TG): Integer; inline;
  129. begin
  130. if FCount = FCapacity then
  131. Self.Expand;
  132. FList^[FCount] := Item;
  133. Result := FCount;
  134. FCount := FCount + 1;
  135. end;
  136. procedure TGList.Clear;
  137. begin
  138. if Assigned(FList) then
  139. begin
  140. SetCount(0);
  141. SetCapacity(0);
  142. FList := nil;
  143. end;
  144. end;
  145. procedure TGList.Delete(Index: Integer); inline;
  146. begin
  147. If (Index<0) or (Index>=FCount) then
  148. Error (SListIndexError, Index);
  149. FCount := FCount-1;
  150. System.Move (FList^[Index+1], FList^[Index], (FCount - Index) * SizeOf(Pointer));
  151. // Shrink the list if appropriate
  152. if (FCapacity > 256) and (FCount < FCapacity shr 2) then
  153. begin
  154. FCapacity := FCapacity shr 1;
  155. ReallocMem(FList, SizeOf(Pointer) * FCapacity);
  156. end;
  157. end;
  158. class procedure TGList.Error(const Msg: string; Data: PtrInt);
  159. begin
  160. Raise EListError.CreateFmt(Msg,[Data]) at get_caller_addr(get_frame);
  161. end;
  162. procedure TGList.Exchange(Index1, Index2: Integer);
  163. var
  164. Temp : Pointer;
  165. begin
  166. If ((Index1 >= FCount) or (Index1 < 0)) then
  167. Error(SListIndexError, Index1);
  168. If ((Index2 >= FCount) or (Index2 < 0)) then
  169. Error(SListIndexError, Index2);
  170. Temp := FList^[Index1];
  171. FList^[Index1] := FList^[Index2];
  172. FList^[Index2] := Temp;
  173. end;
  174. function TGList.Expand: TGList; inline;
  175. var
  176. IncSize : Longint;
  177. begin
  178. if FCount < FCapacity then exit;
  179. IncSize := 4;
  180. if FCapacity > 3 then IncSize := IncSize + 4;
  181. if FCapacity > 8 then IncSize := IncSize+8;
  182. if FCapacity > 127 then Inc(IncSize, FCapacity shr 2);
  183. SetCapacity(FCapacity + IncSize);
  184. Result := Self;
  185. end;
  186. function TGList.First: Pointer;
  187. begin
  188. If FCount = 0 then
  189. Result := Nil
  190. else
  191. Result := Items[0];
  192. end;
  193. function TGList.IndexOf(const Item: TG): Integer;
  194. begin
  195. Result := 0;
  196. while(Result < FCount) and (Flist^[Result] <> Item) do Result := Result + 1;
  197. If Result = FCount then Result := -1;
  198. end;
  199. procedure TGList.Insert(Index: Integer; Item: Pointer); inline;
  200. begin
  201. if (Index < 0) or (Index > FCount )then
  202. Error(SlistIndexError, Index);
  203. iF FCount = FCapacity then Self.Expand;
  204. if Index<FCount then
  205. System.Move(Flist^[Index], Flist^[Index+1], (FCount - Index) * SizeOf(Pointer));
  206. FList^[Index] := Item;
  207. FCount := FCount + 1;
  208. end;
  209. function TGList.Last: Pointer;
  210. begin
  211. { Wouldn't it be better to return nil if the count is zero ?}
  212. If FCount = 0 then
  213. Result := nil
  214. else
  215. Result := Items[FCount - 1];
  216. end;
  217. procedure TGList.Move(CurIndex, NewIndex: Integer);
  218. var
  219. Temp : Pointer;
  220. begin
  221. if ((CurIndex < 0) or (CurIndex > Count - 1)) then
  222. Error(SListIndexError, CurIndex);
  223. if (NewINdex < 0) then
  224. Error(SlistIndexError, NewIndex);
  225. Temp := FList^[CurIndex];
  226. FList^[CurIndex] := nil;
  227. Self.Delete(CurIndex);
  228. Self.Insert(NewIndex, nil);
  229. FList^[NewIndex] := Temp;
  230. end;
  231. function TGList.Remove(const Item: TG): Integer;
  232. begin
  233. Result := IndexOf(Item);
  234. If Result <> -1 then
  235. Self.Delete(Result);
  236. end;
  237. procedure TGList.Pack;
  238. Var
  239. {Last,I,J,}
  240. Runner : Longint;
  241. begin
  242. // Not the fastest; but surely correct
  243. {
  244. for Runner := Fcount - 1 downto 0 do
  245. if Items[Runner] = Nil then
  246. Self.Delete(Runner);
  247. }
  248. { The following may be faster in case of large and defragmented lists
  249. If count=0 then exit;
  250. Runner:=0;I:=0;
  251. TheLast:=Count;
  252. while runner<count do
  253. begin
  254. // Find first Nil
  255. While (FList^[Runner]<>Nil) and (Runner<Count) do Runner:=Runner+1;
  256. if Runner<Count do
  257. begin
  258. // Start searching for non-nil from last known nil+1
  259. if i<Runner then I:=Runner+1;
  260. While (Flist[I]^=Nil) and (I<Count) do I:=I+1;
  261. // Start looking for last non-nil of block.
  262. J:=I+1;
  263. While (Flist^[J]<>Nil) and (J<Count) do J:=J+1;
  264. // Move block and zero out
  265. Move (Flist^[I],Flist^[Runner],J*SizeOf(Pointer));
  266. FillWord (Flist^[I],(J-I)*WordRatio,0);
  267. // Update Runner and Last to point behind last block
  268. TheLast:=Runner+(J-I);
  269. If J=Count then
  270. begin
  271. // Shortcut, when J=Count we checked all pointers
  272. Runner:=Count
  273. else
  274. begin
  275. Runner:=TheLast;
  276. I:=j;
  277. end;
  278. end;
  279. Count:=TheLast;
  280. }
  281. end;
  282. // Needed by Sort method.
  283. Procedure QuickSort(FList: PPointerList; L, R : Longint;
  284. Compare: TListSortCompare);
  285. var
  286. I, J : Longint;
  287. P, Q : Pointer;
  288. begin
  289. repeat
  290. I := L;
  291. J := R;
  292. P := FList^[ (L + R) div 2 ];
  293. repeat
  294. while Compare(P, FList^[i]) > 0 do
  295. I := I + 1;
  296. while Compare(P, FList^[J]) < 0 do
  297. J := J - 1;
  298. If I <= J then
  299. begin
  300. Q := FList^[I];
  301. Flist^[I] := FList^[J];
  302. FList^[J] := Q;
  303. I := I + 1;
  304. J := J - 1;
  305. end;
  306. until I > J;
  307. if L < J then
  308. QuickSort(FList, L, J, Compare);
  309. L := I;
  310. until I >= R;
  311. end;
  312. procedure TGList.Sort(Compare: TListSortCompare);
  313. begin
  314. if Not Assigned(FList) or (FCount < 2) then exit;
  315. QuickSort(Flist, 0, FCount-1, Compare);
  316. end;
  317. procedure TGList.Assign(Obj: TGList);
  318. var
  319. i: Integer;
  320. begin
  321. Clear;
  322. for I := 0 to Obj.Count - 1 do
  323. Add(Obj[i]);
  324. end;
  325. procedure TGList.ForEachCall(proc2call:TListCallback;arg:pointer);
  326. var
  327. i : integer;
  328. p : pointer;
  329. begin
  330. For I:=0 To Count-1 Do
  331. begin
  332. p:=FList^[i];
  333. if assigned(p) then
  334. proc2call(p,arg);
  335. end;
  336. end;
  337. procedure TGList.ForEachCall(proc2call:TListStaticCallback;arg:pointer);
  338. var
  339. i : integer;
  340. p : pointer;
  341. begin
  342. For I:=0 To Count-1 Do
  343. begin
  344. p:=FList^[i];
  345. if assigned(p) then
  346. proc2call(p,arg);
  347. end;
  348. end;
  349. end.