GR32_Containers.pas 19 KB

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