2
0

glinkedlist.pp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. {
  2. This file is part of the Free Pascal FCL library.
  3. Donated in 2013 by Denis Volodarsky
  4. This unit implements a generic double linked list for FPC.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY;without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {
  12. }
  13. unit glinkedlist;
  14. {$MODE DELPHI}
  15. interface
  16. type
  17. // Delphi compatible types.
  18. TCollectionNotification = (cnAdded, cnRemoved, cnExtracted);
  19. TCollectionNotifyEvent<T> = procedure(Sender: TObject; const Item: T;
  20. Action: TCollectionNotification) of object;
  21. type
  22. { TLinkedList }
  23. TLinkedList<T> = class
  24. type
  25. PItem = ^TItem;
  26. TItem = record
  27. private
  28. List: TLinkedList<T>; // owner
  29. public
  30. Data: T;
  31. Prev: PItem;
  32. Next: PItem;
  33. function IsFirst: boolean; inline;
  34. function IsLast: boolean; inline;
  35. function IsSingle: boolean; inline;
  36. function InsertAfter(const Value: T): PItem; inline;
  37. function InsertBefore(const Value: T): PItem; inline;
  38. end;
  39. TTraverseFunc = function(Item: PItem; ud: pointer): boolean;
  40. private
  41. FCount: integer;
  42. FFirst, FLast: PItem;
  43. FOnNotify: TCollectionNotifyEvent<T>;
  44. protected
  45. procedure DoNotify(const Item: T; Action: TCollectionNotification);
  46. procedure Traverse(cb: TTraverseFunc; ud: pointer);
  47. // Following Link/Unlink functions do not modify Count or call Notification.
  48. procedure LinkAfter(Pos, Item: PItem); inline;
  49. procedure LinkBefore(Pos, Item: PItem); inline;
  50. procedure Unlink(Item: PItem); inline;
  51. public
  52. destructor Destroy; override;
  53. procedure Clear;
  54. procedure Delete(Item: PItem);
  55. // Insert Value to start of the list.
  56. function InsertFirst(const Value: T): PItem;
  57. // Insert Value to end of the list.
  58. function InsertLast(const Value: T): PItem;
  59. function InsertAfter(Item: PItem; const Value: T): PItem;
  60. function InsertBefore(Item: PItem; const Value: T): PItem;
  61. // First item moved to end.
  62. procedure RotateLeft;
  63. // Last item moved to begin.
  64. procedure RotateRight;
  65. property Count: integer read FCount;
  66. property First: PItem read FFirst;
  67. property Last: PItem read FLast;
  68. property OnNotify: TCollectionNotifyEvent<T> read FOnNotify write FOnNotify;
  69. type
  70. { TEnumerator }
  71. TEnumerator = class
  72. private
  73. FList: TLinkedList<T>;
  74. FCurrent: PItem;
  75. protected
  76. function DoGetCurrent: T;
  77. function DoMoveNext: boolean;
  78. public
  79. constructor Create(AList: TLinkedList<T>);
  80. function MoveNext: boolean;
  81. property Current: T read DoGetCurrent;
  82. end;
  83. function GetEnumerator: TEnumerator; reintroduce;
  84. end;
  85. implementation
  86. { TLinkedList<T>.TItem }
  87. function TLinkedList<T>.TItem.InsertAfter(const Value: T): PItem;
  88. begin
  89. Result := List.InsertAfter(@self, Value);
  90. end;
  91. function TLinkedList<T>.TItem.InsertBefore(const Value: T): PItem;
  92. begin
  93. Result := List.InsertBefore(@self, Value);
  94. end;
  95. function TLinkedList<T>.TItem.IsFirst: boolean;
  96. begin
  97. Result := not Assigned(Prev);
  98. end;
  99. function TLinkedList<T>.TItem.IsLast: boolean;
  100. begin
  101. Result := not Assigned(Next);
  102. end;
  103. function TLinkedList<T>.TItem.IsSingle: boolean;
  104. begin
  105. Result := IsFirst and IsLast;
  106. end;
  107. { TLinkedList<T> }
  108. destructor TLinkedList<T>.Destroy;
  109. begin
  110. Clear;
  111. Inherited;
  112. end;
  113. procedure TLinkedList<T>.DoNotify(const Item: T; Action: TCollectionNotification);
  114. begin
  115. if Assigned(FOnNotify) then
  116. FOnNotify(self, Item, Action);
  117. end;
  118. procedure TLinkedList<T>.Clear;
  119. var
  120. Next: PItem;
  121. OldValue: T;
  122. begin
  123. if (FCount <> 0) then
  124. begin
  125. while Assigned(FFirst) do
  126. begin
  127. OldValue := FFirst^.Data;
  128. Next := FFirst^.Next;
  129. Dispose(FFirst);
  130. FFirst := Next;
  131. if FFirst = nil then
  132. FLast := nil;
  133. dec(FCount);
  134. DoNotify(OldValue, cnRemoved);
  135. end;
  136. end;
  137. end;
  138. procedure TLinkedList<T>.Delete(Item: PItem);
  139. begin
  140. if Assigned(Item) then
  141. begin
  142. Unlink(Item);
  143. Dec(FCount);
  144. DoNotify(Item^.Data, cnRemoved);
  145. Dispose(Item);
  146. end;
  147. end;
  148. procedure TLinkedList<T>.Traverse(cb: TTraverseFunc; ud: pointer);
  149. var
  150. Cur, Next: PItem;
  151. begin
  152. if Assigned(cb) then
  153. begin
  154. Cur := First;
  155. while Assigned(Cur) do
  156. begin
  157. Next := Cur^.Next;
  158. if not cb(Cur, ud) then
  159. break;
  160. Cur := Next;
  161. end;
  162. end;
  163. end;
  164. procedure TLinkedList<T>.Unlink(Item: PItem);
  165. begin
  166. if Item^.IsFirst then
  167. FFirst := Item^.Next
  168. else
  169. Item^.Prev^.Next := Item^.Next;
  170. if Item^.IsLast then
  171. FLast := Item^.Prev
  172. else
  173. Item^.Next^.Prev := Item^.Prev;
  174. end;
  175. function TLinkedList<T>.InsertFirst(const Value: T): PItem;
  176. begin
  177. if FCount <> 0 then
  178. Exit(InsertBefore(FFirst, Value));
  179. // List is empty: add first item.
  180. new(Result);
  181. Result^.List := self;
  182. Result^.Data := Value;
  183. Result^.Prev := nil;
  184. Result^.Next := nil;
  185. FFirst := Result;
  186. FLast := Result;
  187. inc(FCount);
  188. DoNotify(Value, cnAdded);
  189. end;
  190. function TLinkedList<T>.InsertAfter(Item: PItem; const Value: T): PItem;
  191. begin
  192. if Assigned(Item) then
  193. begin
  194. new(Result);
  195. Result^.List := self;
  196. Result^.Data := Value;
  197. LinkAfter(Item, Result);
  198. inc(FCount);
  199. DoNotify(Value, cnAdded);
  200. Exit;
  201. end;
  202. Exit(nil);
  203. end;
  204. function TLinkedList<T>.InsertBefore(Item: PItem; const Value: T): PItem;
  205. begin
  206. if Assigned(Item) then
  207. begin
  208. new(Result);
  209. Result^.List := self;
  210. Result^.Data := Value;
  211. LinkBefore(Item, Result);
  212. inc(FCount);
  213. DoNotify(Value, cnAdded);
  214. Exit;
  215. end;
  216. Exit(nil);
  217. end;
  218. function TLinkedList<T>.InsertLast(const Value: T): PItem;
  219. begin
  220. if FCount = 0 then
  221. Result := InsertFirst(Value)
  222. else
  223. Result := InsertAfter(FLast, Value);
  224. end;
  225. procedure TLinkedList<T>.LinkAfter(Pos, Item: PItem);
  226. var
  227. PosNext: PItem;
  228. begin
  229. PosNext := Pos^.Next;
  230. Pos^.Next := Item;
  231. if Assigned(PosNext) then
  232. PosNext^.Prev := Item
  233. else
  234. FLast := Item;
  235. Item^.Prev := Pos;
  236. Item^.Next := PosNext;
  237. end;
  238. procedure TLinkedList<T>.LinkBefore(Pos, Item: PItem);
  239. var
  240. PosPrev: PItem;
  241. begin
  242. PosPrev := Pos^.Prev;
  243. Pos^.Prev := Item;
  244. if Assigned(PosPrev) then
  245. PosPrev^.Next := Item
  246. else
  247. FFirst := Item;
  248. Item^.Prev := PosPrev;
  249. Item^.Next := Pos;
  250. end;
  251. procedure TLinkedList<T>.RotateLeft;
  252. var
  253. tmp: PItem;
  254. begin
  255. if FCount > 1 then
  256. begin
  257. tmp := FFirst;
  258. Unlink(tmp);
  259. LinkAfter(FLast, tmp);
  260. end;
  261. end;
  262. procedure TLinkedList<T>.RotateRight;
  263. var
  264. tmp: PItem;
  265. begin
  266. if FCount > 1 then
  267. begin
  268. tmp := FLast;
  269. Unlink(tmp);
  270. LinkBefore(FFirst, tmp);
  271. end;
  272. end;
  273. constructor TLinkedList<T>.TEnumerator.Create(AList: TLinkedList<T>);
  274. begin
  275. inherited Create;
  276. FList := AList;
  277. FCurrent := nil;
  278. end;
  279. function TLinkedList<T>.TEnumerator.MoveNext: boolean;
  280. begin
  281. Result := DoMoveNext;
  282. end;
  283. function TLinkedList<T>.TEnumerator.DoGetCurrent: T;
  284. begin
  285. Result := FCurrent.Data;
  286. end;
  287. function TLinkedList<T>.TEnumerator.DoMoveNext: boolean;
  288. begin
  289. if not Assigned(FCurrent) then
  290. begin
  291. FCurrent := FList.First;
  292. Result := Assigned(FCurrent);
  293. Exit;
  294. end;
  295. Result := Assigned(FCurrent.Next);
  296. if Result then
  297. FCurrent := FCurrent.Next;
  298. end;
  299. function TLinkedList<T>.GetEnumerator: TEnumerator;
  300. begin
  301. Result := TEnumerator.Create(self);
  302. end;
  303. end.