GR32_Containers.pas 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854
  1. unit GR32_Containers;
  2. (* ***** BEGIN LICENSE BLOCK *****
  3. * Version: MPL 1.1 or LGPL 2.1 with linking exception
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. * http://www.mozilla.org/MPL/
  9. *
  10. * Software distributed under the License is distributed on an "AS IS" basis,
  11. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. * for the specific language governing rights and limitations under the
  13. * License.
  14. *
  15. * Alternatively, the contents of this file may be used under the terms of the
  16. * Free Pascal modified version of the GNU Lesser General Public License
  17. * Version 2.1 (the "FPC modified LGPL License"), in which case the provisions
  18. * of this license are applicable instead of those above.
  19. * Please see the file LICENSE.txt for additional information concerning this
  20. * license.
  21. *
  22. * The Original Code is Repaint Optimizer Extension for Graphics32
  23. *
  24. * The Initial Developer of the Original Code is
  25. * Andre Beckedorf - metaException
  26. * [email protected]
  27. *
  28. * Portions created by the Initial Developer are Copyright (C) 2005-2009
  29. * the Initial Developer. All Rights Reserved.
  30. *
  31. * Contributor(s):
  32. *
  33. * ***** END LICENSE BLOCK ***** *)
  34. interface
  35. {$I GR32.inc}
  36. uses
  37. System.Types,
  38. RTLConsts,
  39. GR32, SysUtils, Classes, TypInfo;
  40. const
  41. BUCKET_MASK = $FF;
  42. BUCKET_COUNT = BUCKET_MASK + 1; // 256 buckets by default
  43. type
  44. PPItem = ^PItem;
  45. PItem = Pointer;
  46. PPData = ^PData;
  47. PData = Pointer;
  48. PPointerBucketItem = ^TPointerBucketItem;
  49. TPointerBucketItem = record
  50. Item: PItem;
  51. Data: PData;
  52. end;
  53. TPointerBucketItemArray = array of TPointerBucketItem;
  54. TPointerBucket = record
  55. Count: Integer;
  56. Items: TPointerBucketItemArray;
  57. end;
  58. TPointerBucketArray = array[0..BUCKET_MASK] of TPointerBucket;
  59. { TPointerMap }
  60. { Associative pointer map
  61. Inspired by TBucketList, which is not available on D5/CB5, it is
  62. reimplemented from scratch, simple, optimized and light-weight.
  63. Not thread-safe. Does use exceptions only for Data property. }
  64. TPointerMap = class
  65. private
  66. FBuckets: TPointerBucketArray;
  67. FCount: Integer;
  68. protected
  69. function GetData(Item: PItem): PData;
  70. procedure SetData(Item: PItem; const Data: PData);
  71. function Exists(Item: Pointer; out BucketIndex, ItemIndex: Integer): Boolean;
  72. function Delete(BucketIndex, ItemIndex: Integer): PData; virtual;
  73. public
  74. destructor Destroy; override;
  75. function Add(NewItem: PItem): PPData; overload;
  76. function Add(NewItem: PItem; out IsNew: Boolean): PPData; overload;
  77. function Add(NewItem: PItem; NewData: PData): PPData; overload;
  78. function Add(NewItem: PItem; NewData: PData; out IsNew: Boolean): PPData; overload;
  79. function Remove(Item: PItem): PData;
  80. procedure Clear;
  81. function Contains(Item: PItem): Boolean;
  82. function Find(Item: PItem; out Data: PPData): Boolean;
  83. property Data[Item: PItem]: PData read GetData write SetData; default;
  84. property Count: Integer read FCount;
  85. end;
  86. { TPointerMapIterator }
  87. { Iterator object for the associative pointer map
  88. See below for usage example... }
  89. TPointerMapIterator = class
  90. private
  91. FSrcPointerMap: TPointerMap;
  92. FItem: PItem;
  93. FData: PData;
  94. FCurBucketIndex: Integer;
  95. FCurItemIndex: Integer;
  96. public
  97. constructor Create(SrcPointerMap: TPointerMap);
  98. function Next: Boolean;
  99. property Item: PItem read FItem;
  100. property Data: PData read FData;
  101. end;
  102. {
  103. USAGE EXAMPLE:
  104. --------------
  105. with TPointerMapIterator.Create(MyPointerMap) do
  106. try
  107. while Next do
  108. begin
  109. // do something with Item and Data here...
  110. end;
  111. finally
  112. Free;
  113. end;
  114. }
  115. PPolyRects = ^TPolyRects;
  116. TPolyRects = Array[0..Maxint div 32 - 1] of TRect;
  117. { TRectList }
  118. { List that holds Rects
  119. Do not reuse TList due to pointer structure.
  120. A direct structure is more memory efficient.
  121. stripped version of TList blatantly stolen from Classes.pas }
  122. TRectList = class
  123. private
  124. FList: PPolyRects;
  125. FCount: Integer;
  126. FCapacity: Integer;
  127. protected
  128. function Get(Index: Integer): PRect;
  129. procedure Grow; virtual;
  130. procedure SetCapacity(NewCapacity: Integer);
  131. procedure SetCount(NewCount: Integer);
  132. public
  133. destructor Destroy; override;
  134. function Add(const Rect: TRect): Integer;
  135. procedure Clear; virtual;
  136. procedure Delete(Index: Integer);
  137. procedure Exchange(Index1, Index2: Integer);
  138. function IndexOf(const Rect: TRect): Integer;
  139. procedure Insert(Index: Integer; const Rect: TRect);
  140. procedure Move(CurIndex, NewIndex: Integer);
  141. function Remove(const Rect: TRect): Integer;
  142. procedure Pack;
  143. property Capacity: Integer read FCapacity write SetCapacity;
  144. property Count: Integer read FCount write SetCount;
  145. property Items[Index: Integer]: PRect read Get; default;
  146. property List: PPolyRects read FList;
  147. end;
  148. { TClassList }
  149. { This is a class that maintains a list of classes. }
  150. TClassList = class(TList)
  151. protected
  152. function GetItems(Index: Integer): TClass;
  153. procedure SetItems(Index: Integer; AClass: TClass);
  154. public
  155. function Add(AClass: TClass): Integer;
  156. function Extract(Item: TClass): TClass;
  157. function Remove(AClass: TClass): Integer;
  158. function IndexOf(AClass: TClass): Integer;
  159. function First: TClass;
  160. function Last: TClass;
  161. function Find(const AClassName: string): TClass;
  162. procedure GetClassNames(Strings: TStrings);
  163. procedure Insert(Index: Integer; AClass: TClass);
  164. property Items[Index: Integer]: TClass read GetItems write SetItems; default;
  165. end;
  166. PLinkedNode = ^TLinkedNode;
  167. TLinkedNode = record
  168. Prev: PLinkedNode;
  169. Next: PLinkedNode;
  170. Data: Pointer;
  171. end;
  172. TIteratorProc = procedure(Node: PLinkedNode; Index: Integer);
  173. TFreeDataEvent = procedure(Data: Pointer) of object;
  174. { TLinkedList }
  175. { A class for maintaining a linked list }
  176. TLinkedList = class
  177. private
  178. FCount: Integer;
  179. FHead: PLinkedNode;
  180. FTail: PLinkedNode;
  181. FOnFreeData: TFreeDataEvent;
  182. protected
  183. procedure DoFreeData(Data: Pointer); virtual;
  184. public
  185. destructor Destroy; override;
  186. function Add: PLinkedNode;
  187. procedure Remove(Node: PLinkedNode);
  188. function IndexOf(Node: PLinkedNode): Integer;
  189. function GetNode(Index: Integer): PLinkedNode;
  190. procedure Exchange(Node1, Node2: PLinkedNode);
  191. procedure InsertBefore(Node, NewNode: PLinkedNode);
  192. procedure InsertAfter(Node, NewNode: PLinkedNode);
  193. procedure Clear;
  194. procedure IterateList(CallBack: TIteratorProc);
  195. property Head: PLinkedNode read FHead write FHead;
  196. property Tail: PLinkedNode read FTail write FTail;
  197. property Count: Integer read FCount write FCount;
  198. property OnFreeData: TFreeDataEvent read FOnFreeData write FOnFreeData;
  199. end;
  200. procedure SmartAssign(Src, Dst: TPersistent; TypeKinds: TTypeKinds = tkProperties);
  201. procedure Advance(var Node: PLinkedNode; Steps: Integer = 1);
  202. implementation
  203. uses
  204. GR32_LowLevel;
  205. procedure SmartAssign(Src, Dst: TPersistent; TypeKinds: TTypeKinds = tkProperties);
  206. var
  207. Count, I: Integer;
  208. Props: PPropList;
  209. SubSrc, SubDst: TPersistent;
  210. begin
  211. Count := GetTypeData(Src.ClassInfo).PropCount;
  212. if Count = 0 then Exit;
  213. GetMem(Props, Count * SizeOf(PPropInfo));
  214. try
  215. // Get the property list in an unsorted fashion.
  216. // This is important so the order in which the properties are defined is obeyed,
  217. // ie. mimic how the Delphi form loader would set the properties.
  218. Count := GetPropList(Src.ClassInfo, TypeKinds, Props, False);
  219. for I := 0 to Count - 1 do
  220. with Props^[I]^ do
  221. begin
  222. if PropType^.Kind = tkClass then
  223. begin
  224. // TODO DVT Added cast to fix ShortString to String warnings. Need to verify is OK
  225. SubDst := TPersistent(GetObjectProp(Dst, Props^[I]));
  226. if not Assigned(SubDst) then Continue;
  227. SubSrc := TPersistent(GetObjectProp(Src, Props^[I]));
  228. if Assigned(SubSrc) then SubDst.Assign(SubSrc);
  229. end
  230. else
  231. SetPropValue(Dst, Props^[I], GetPropValue(Src, Props^[I], True));
  232. end;
  233. finally
  234. FreeMem(Props, Count * SizeOf(PPropInfo));
  235. end;
  236. end;
  237. procedure Advance(var Node: PLinkedNode; Steps: Integer);
  238. begin
  239. if Steps > 0 then
  240. begin
  241. while Assigned(Node) and (Steps > 0) do
  242. begin
  243. Dec(Steps);
  244. Node := Node.Next;
  245. end;
  246. end
  247. else
  248. begin
  249. while Assigned(Node) and (Steps < 0) do
  250. begin
  251. Inc(Steps);
  252. Node := Node.Prev;
  253. end;
  254. end;
  255. end;
  256. { TPointerMap }
  257. function TPointerMap.Add(NewItem: PItem; NewData: PData): PPData;
  258. var
  259. Dummy: Boolean;
  260. begin
  261. Result := Add(NewItem, NewData, Dummy);
  262. end;
  263. function TPointerMap.Add(NewItem: PItem): PPData;
  264. var
  265. Dummy: Boolean;
  266. begin
  267. Result := Add(NewItem, nil, Dummy);
  268. end;
  269. function TPointerMap.Add(NewItem: PItem; out IsNew: Boolean): PPData;
  270. begin
  271. Result := Add(NewItem, nil, IsNew);
  272. end;
  273. function TPointerMap.Add(NewItem: PItem; NewData: PData; out IsNew: Boolean): PPData;
  274. var
  275. BucketIndex, ItemIndex, Capacity: Integer;
  276. begin
  277. if Exists(NewItem, BucketIndex, ItemIndex) then
  278. begin
  279. IsNew := False;
  280. Result := @FBuckets[BucketIndex].Items[ItemIndex].Data
  281. end
  282. else
  283. begin
  284. with FBuckets[BucketIndex] do
  285. begin
  286. Capacity := Length(Items);
  287. // enlarge capacity if completely used
  288. if Count = Capacity then
  289. begin
  290. if Capacity > 64 then
  291. Inc(Capacity, Capacity div 4)
  292. else if Capacity > 8 then
  293. Inc(Capacity, 16)
  294. else
  295. Inc(Capacity, 4);
  296. SetLength(Items, Capacity);
  297. end;
  298. with Items[Count] do
  299. begin
  300. Item := NewItem;
  301. Data := NewData;
  302. Result := @Data;
  303. end;
  304. Inc(Count);
  305. IsNew := True;
  306. end;
  307. Inc(FCount);
  308. end;
  309. end;
  310. procedure TPointerMap.Clear;
  311. var
  312. BucketIndex, ItemIndex: Integer;
  313. begin
  314. FCount := 0;
  315. for BucketIndex := 0 to BUCKET_MASK do
  316. with FBuckets[BucketIndex] do
  317. begin
  318. for ItemIndex := Count - 1 downto 0 do
  319. Delete(BucketIndex, ItemIndex);
  320. Count := 0;
  321. SetLength(Items, 0);
  322. end;
  323. end;
  324. destructor TPointerMap.Destroy;
  325. begin
  326. Clear;
  327. inherited Destroy;
  328. end;
  329. function TPointerMap.Delete(BucketIndex, ItemIndex: Integer): PData;
  330. begin
  331. with FBuckets[BucketIndex] do
  332. begin
  333. Result := Items[ItemIndex].Data;
  334. if FCount = 0 then Exit;
  335. Dec(Count);
  336. if Count = 0 then
  337. SetLength(Items, 0)
  338. else
  339. if (ItemIndex < Count) then
  340. Move(Items[ItemIndex + 1], Items[ItemIndex], (Count - ItemIndex) * SizeOf(TPointerBucketItem));
  341. end;
  342. Dec(FCount);
  343. end;
  344. function TPointerMap.Remove(Item: PItem): PData;
  345. var
  346. BucketIndex, ItemIndex: Integer;
  347. begin
  348. if Exists(Item, BucketIndex, ItemIndex) then
  349. Result := Delete(BucketIndex, ItemIndex)
  350. else
  351. Result := nil;
  352. end;
  353. function TPointerMap.Contains(Item: PItem): Boolean;
  354. var
  355. Dummy: Integer;
  356. begin
  357. Result := Exists(Item, Dummy, Dummy);
  358. end;
  359. function TPointerMap.Find(Item: PItem; out Data: PPData): Boolean;
  360. var
  361. BucketIndex, ItemIndex: Integer;
  362. begin
  363. Result := Exists(Item, BucketIndex, ItemIndex);
  364. if Result then
  365. Data := @FBuckets[BucketIndex].Items[ItemIndex].Data;
  366. end;
  367. function TPointerMap.Exists(Item: PItem; out BucketIndex, ItemIndex: Integer): Boolean;
  368. var
  369. I: Integer;
  370. begin
  371. {$IFDEF HAS_NATIVEINT}
  372. BucketIndex := NativeUInt(Item) shr 8 and BUCKET_MASK; // KISS pointer hash(TM)
  373. {$ELSE}
  374. BucketIndex := Cardinal(Item) shr 8 and BUCKET_MASK; // KISS pointer hash(TM)
  375. {$ENDIF}
  376. // due to their randomness, pointers most commonly differ at byte 1, we use
  377. // this characteristic for our hash and just apply the mask to it.
  378. // Worst case scenario happens when most changes are at byte 0, which causes
  379. // one bucket to be saturated whereas the other buckets are almost empty...
  380. Result := False;
  381. with FBuckets[BucketIndex] do
  382. for I := 0 to Count - 1 do
  383. if Items[I].Item = Item then
  384. begin
  385. ItemIndex := I;
  386. Result := True;
  387. Exit;
  388. end;
  389. end;
  390. function TPointerMap.GetData(Item: PItem): PData;
  391. var
  392. BucketIndex, ItemIndex: Integer;
  393. begin
  394. if not Exists(Item, BucketIndex, ItemIndex) then
  395. {$IFDEF FPC}
  396. raise EListError.CreateFmt(SItemNotFound, [Item])
  397. {$ELSE}
  398. {$IFDEF HAS_NATIVEINT}
  399. raise EListError.CreateFmt(SItemNotFound, [NativeInt(Item)])
  400. {$ELSE}
  401. raise EListError.CreateFmt(SItemNotFound, [Integer(Item)])
  402. {$ENDIF}
  403. {$ENDIF}
  404. else
  405. Result := FBuckets[BucketIndex].Items[ItemIndex].Data;
  406. end;
  407. procedure TPointerMap.SetData(Item: PItem; const Data: PData);
  408. var
  409. BucketIndex, ItemIndex: Integer;
  410. begin
  411. if not Exists(Item, BucketIndex, ItemIndex) then
  412. {$IFDEF FPC}
  413. raise EListError.CreateFmt(SItemNotFound, [Item])
  414. {$ELSE}
  415. {$IFDEF HAS_NATIVEINT}
  416. raise EListError.CreateFmt(SItemNotFound, [NativeInt(Item)])
  417. {$ELSE}
  418. raise EListError.CreateFmt(SItemNotFound, [Integer(Item)])
  419. {$ENDIF}
  420. {$ENDIF}
  421. else
  422. FBuckets[BucketIndex].Items[ItemIndex].Data := Data;
  423. end;
  424. { TPointerMapIterator }
  425. constructor TPointerMapIterator.Create(SrcPointerMap: TPointerMap);
  426. begin
  427. inherited Create;
  428. FSrcPointerMap := SrcPointerMap;
  429. FCurBucketIndex := -1;
  430. FCurItemIndex := -1;
  431. end;
  432. function TPointerMapIterator.Next: Boolean;
  433. begin
  434. if FCurItemIndex > 0 then
  435. Dec(FCurItemIndex)
  436. else
  437. begin
  438. FCurItemIndex := -1;
  439. while (FCurBucketIndex < BUCKET_MASK) and (FCurItemIndex = -1) do
  440. begin
  441. Inc(FCurBucketIndex);
  442. FCurItemIndex := FSrcPointerMap.FBuckets[FCurBucketIndex].Count - 1;
  443. end;
  444. if FCurBucketIndex = BUCKET_MASK then
  445. begin
  446. Result := False;
  447. Exit;
  448. end
  449. end;
  450. Result := True;
  451. with FSrcPointerMap.FBuckets[FCurBucketIndex].Items[FCurItemIndex] do
  452. begin
  453. FItem := Item;
  454. FData := Data;
  455. end;
  456. end;
  457. { TRectList }
  458. destructor TRectList.Destroy;
  459. begin
  460. SetCount(0);
  461. SetCapacity(0);
  462. end;
  463. function TRectList.Add(const Rect: TRect): Integer;
  464. begin
  465. Result := FCount;
  466. if Result = FCapacity then
  467. Grow;
  468. FList^[Result] := Rect;
  469. Inc(FCount);
  470. end;
  471. procedure TRectList.Clear;
  472. begin
  473. SetCount(0);
  474. SetCapacity(10);
  475. end;
  476. procedure TRectList.Delete(Index: Integer);
  477. begin
  478. Dec(FCount);
  479. if Index < FCount then
  480. System.Move(FList^[Index + 1], FList^[Index],
  481. (FCount - Index) * SizeOf(TRect));
  482. end;
  483. procedure TRectList.Exchange(Index1, Index2: Integer);
  484. var
  485. Item: TRect;
  486. begin
  487. Item := FList^[Index1];
  488. FList^[Index1] := FList^[Index2];
  489. FList^[Index2] := Item;
  490. end;
  491. function TRectList.Get(Index: Integer): PRect;
  492. begin
  493. if (Index < 0) or (Index >= FCount) then
  494. Result := nil
  495. else
  496. Result := @FList^[Index];
  497. end;
  498. procedure TRectList.Grow;
  499. var
  500. Delta: Integer;
  501. begin
  502. if FCapacity > 128 then
  503. Delta := FCapacity div 4
  504. else
  505. if FCapacity > 8 then
  506. Delta := 32
  507. else
  508. Delta := 8;
  509. SetCapacity(FCapacity + Delta);
  510. end;
  511. function TRectList.IndexOf(const Rect: TRect): Integer;
  512. begin
  513. Result := 0;
  514. while (Result < FCount) and not GR32.EqualRect(FList^[Result], Rect) do
  515. Inc(Result);
  516. if Result = FCount then
  517. Result := -1;
  518. end;
  519. procedure TRectList.Insert(Index: Integer; const Rect: TRect);
  520. begin
  521. if FCount = FCapacity then
  522. Grow;
  523. if Index < FCount then
  524. System.Move(FList^[Index], FList^[Index + 1],
  525. (FCount - Index) * SizeOf(TRect));
  526. FList^[Index] := Rect;
  527. Inc(FCount);
  528. end;
  529. procedure TRectList.Move(CurIndex, NewIndex: Integer);
  530. var
  531. Item: TRect;
  532. begin
  533. if CurIndex <> NewIndex then
  534. begin
  535. Item := Get(CurIndex)^;
  536. Delete(CurIndex);
  537. Insert(NewIndex, Item);
  538. end;
  539. end;
  540. function TRectList.Remove(const Rect: TRect): Integer;
  541. begin
  542. Result := IndexOf(Rect);
  543. if Result >= 0 then
  544. Delete(Result);
  545. end;
  546. procedure TRectList.Pack;
  547. var
  548. I: Integer;
  549. begin
  550. for I := FCount - 1 downto 0 do
  551. if Items[I] = nil then
  552. Delete(I);
  553. end;
  554. procedure TRectList.SetCapacity(NewCapacity: Integer);
  555. begin
  556. if NewCapacity <> FCapacity then
  557. begin
  558. ReallocMem(FList, NewCapacity * SizeOf(TRect));
  559. FCapacity := NewCapacity;
  560. end;
  561. end;
  562. procedure TRectList.SetCount(NewCount: Integer);
  563. var
  564. I: Integer;
  565. begin
  566. if NewCount > FCapacity then
  567. SetCapacity(NewCount);
  568. if NewCount > FCount then
  569. FillChar(FList^[FCount], (NewCount - FCount) * SizeOf(TRect), 0)
  570. else
  571. for I := FCount - 1 downto NewCount do
  572. Delete(I);
  573. FCount := NewCount;
  574. end;
  575. { TClassList }
  576. function TClassList.Add(AClass: TClass): Integer;
  577. begin
  578. Result := inherited Add(AClass);
  579. end;
  580. function TClassList.Extract(Item: TClass): TClass;
  581. begin
  582. Result := TClass(inherited Extract(Item));
  583. end;
  584. function TClassList.Find(const AClassName: string): TClass;
  585. var
  586. I: Integer;
  587. begin
  588. Result := nil;
  589. for I := 0 to Count - 1 do
  590. if TClass(List[I]).ClassName = AClassName then
  591. begin
  592. Result := TClass(List[I]);
  593. Break;
  594. end;
  595. end;
  596. function TClassList.First: TClass;
  597. begin
  598. Result := TClass(inherited First);
  599. end;
  600. procedure TClassList.GetClassNames(Strings: TStrings);
  601. var
  602. I: Integer;
  603. begin
  604. for I := 0 to Count - 1 do
  605. Strings.Add(TClass(List[I]).ClassName);
  606. end;
  607. function TClassList.GetItems(Index: Integer): TClass;
  608. begin
  609. Result := TClass(inherited Items[Index]);
  610. end;
  611. function TClassList.IndexOf(AClass: TClass): Integer;
  612. begin
  613. Result := inherited IndexOf(AClass);
  614. end;
  615. procedure TClassList.Insert(Index: Integer; AClass: TClass);
  616. begin
  617. inherited Insert(Index, AClass);
  618. end;
  619. function TClassList.Last: TClass;
  620. begin
  621. Result := TClass(inherited Last);
  622. end;
  623. function TClassList.Remove(AClass: TClass): Integer;
  624. begin
  625. Result := inherited Remove(AClass);
  626. end;
  627. procedure TClassList.SetItems(Index: Integer; AClass: TClass);
  628. begin
  629. inherited Items[Index] := AClass;
  630. end;
  631. { TLinkedList }
  632. function TLinkedList.Add: PLinkedNode;
  633. begin
  634. New(Result);
  635. Result.Data := nil;
  636. Result.Next := nil;
  637. Result.Prev := nil;
  638. if Head = nil then
  639. begin
  640. Head := Result;
  641. Tail := Result;
  642. end
  643. else
  644. InsertAfter(FTail, Result);
  645. end;
  646. procedure TLinkedList.Clear;
  647. var
  648. P, NextP: PLinkedNode;
  649. begin
  650. P := Head;
  651. while Assigned(P) do
  652. begin
  653. NextP := P.Next;
  654. DoFreeData(P.Data);
  655. Dispose(P);
  656. P := NextP;
  657. end;
  658. Head := nil;
  659. Tail := nil;
  660. Count := 0;
  661. end;
  662. destructor TLinkedList.Destroy;
  663. begin
  664. Clear;
  665. end;
  666. procedure TLinkedList.DoFreeData(Data: Pointer);
  667. begin
  668. if Assigned(FOnFreeData) then FOnFreeData(Data);
  669. end;
  670. procedure TLinkedList.Exchange(Node1, Node2: PLinkedNode);
  671. begin
  672. if Assigned(Node1) and Assigned(Node2) and (Node1 <> Node2) then
  673. begin
  674. if Assigned(Node1.Prev) then Node1.Prev.Next := Node2;
  675. if Assigned(Node1.Next) then Node1.Next.Prev := Node2;
  676. if Assigned(Node2.Prev) then Node2.Prev.Next := Node1;
  677. if Assigned(Node2.Next) then Node2.Next.Prev := Node1;
  678. if Head = Node1 then Head := Node2 else if Head = Node2 then Head := Node1;
  679. if Tail = Node1 then Tail := Node2 else if Tail = Node2 then Tail := Node1;
  680. Swap(Pointer(Node1.Next), Pointer(Node2.Next));
  681. Swap(Pointer(Node1.Prev), Pointer(Node2.Prev));
  682. end;
  683. end;
  684. function TLinkedList.GetNode(Index: Integer): PLinkedNode;
  685. begin
  686. Result := Head;
  687. Advance(Result, Index);
  688. end;
  689. function TLinkedList.IndexOf(Node: PLinkedNode): Integer;
  690. var
  691. I: Integer;
  692. P: PLinkedNode;
  693. begin
  694. Result := -1;
  695. P := Head;
  696. for I := 0 to Count - 1 do
  697. begin
  698. if P = Node then
  699. begin
  700. Result := I;
  701. Exit;
  702. end;
  703. P := P.Next;
  704. end;
  705. end;
  706. procedure TLinkedList.InsertAfter(Node, NewNode: PLinkedNode);
  707. begin
  708. if Assigned(Node) and Assigned(NewNode) then
  709. begin
  710. NewNode.Prev := Node;
  711. NewNode.Next := Node.Next;
  712. if Assigned(Node.Next) then Node.Next.Prev := NewNode;
  713. Node.Next := NewNode;
  714. if Node = Tail then Tail := NewNode;
  715. Inc(FCount);
  716. end;
  717. end;
  718. procedure TLinkedList.InsertBefore(Node, NewNode: PLinkedNode);
  719. begin
  720. if Assigned(Node) and Assigned(NewNode) then
  721. begin
  722. NewNode.Next := Node;
  723. NewNode.Prev := Node.Prev;
  724. if Assigned(Node.Prev) then Node.Prev.Next := NewNode;
  725. Node.Prev := NewNode;
  726. if Node = Head then Head := NewNode;
  727. Inc(FCount);
  728. end;
  729. end;
  730. procedure TLinkedList.IterateList(CallBack: TIteratorProc);
  731. var
  732. I: Integer;
  733. P: PLinkedNode;
  734. begin
  735. P := Head;
  736. for I := 0 to Count - 1 do
  737. begin
  738. CallBack(P, I);
  739. P := P.Next;
  740. end;
  741. end;
  742. procedure TLinkedList.Remove(Node: PLinkedNode);
  743. begin
  744. if Assigned(Node) then
  745. begin
  746. DoFreeData(Node.Data);
  747. if Assigned(Node.Prev) then Node.Prev.Next := Node.Next;
  748. if Assigned(Node.Next) then Node.Next.Prev := Node.Prev;
  749. if Node = Head then Head := Node.Next;
  750. if Node = Tail then Tail := Node.Prev;
  751. Dispose(Node);
  752. Dec(FCount);
  753. end;
  754. end;
  755. end.