
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@6159 8e941d3f-bd1b-0410-a28a-d453659cc2b4
1051 lines
23 KiB
ObjectPascal
1051 lines
23 KiB
ObjectPascal
// Upgraded to Delphi 2009: Sebastian Zierer
|
|
|
|
(* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is TurboPower SysTools
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* TurboPower Software
|
|
*
|
|
* Portions created by the Initial Developer are Copyright (C) 1996-2002
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* ***** END LICENSE BLOCK ***** *)
|
|
|
|
{*********************************************************}
|
|
{* SysTools: StList.pas 4.04 *}
|
|
{*********************************************************}
|
|
{* SysTools: Linked list class *}
|
|
{*********************************************************}
|
|
|
|
{$IFDEF FPC}
|
|
{$mode DELPHI}
|
|
{$ENDIF}
|
|
|
|
//{$I StDefine.inc}
|
|
|
|
{Notes:
|
|
Nodes stored in the list can be of type TStListNode or of a derived type.
|
|
Pass the node class to the list constructor.
|
|
|
|
TStList is a doubly-linked list that can be scanned backward just as
|
|
efficiently as forward.
|
|
|
|
The list retains the index and node of the last node found by Nth (or by
|
|
the indexed array property). This makes For loops that scan a list much
|
|
faster and speeds up random calls to Nth by about a factor of two.
|
|
}
|
|
|
|
unit StList;
|
|
|
|
interface
|
|
|
|
uses
|
|
{$IFNDEF FPC}
|
|
Windows,
|
|
{$ENDIF}
|
|
SysUtils, Classes,
|
|
StConst, StBase;
|
|
|
|
type
|
|
TStListNode = class(TStNode)
|
|
{.Z+}
|
|
protected
|
|
FNext : TStListNode; {Next node}
|
|
FPrev : TStListNode; {Previous node}
|
|
|
|
{.Z-}
|
|
public
|
|
constructor Create(AData : Pointer); override;
|
|
{-Initialize node}
|
|
end;
|
|
|
|
TStList = class(TStContainer)
|
|
{.Z+}
|
|
protected
|
|
{property instance variables}
|
|
FHead : TStListNode; {Start of list}
|
|
FTail : TStListNode; {End of list}
|
|
|
|
{private instance variables}
|
|
lsLastI : LongInt; {Last index requested from Nth}
|
|
lsLastP : TStListNode; {Last node returned by Nth}
|
|
|
|
{protected undocumented methods}
|
|
procedure ForEachPointer(Action : TIteratePointerFunc;
|
|
OtherData : pointer);
|
|
override;
|
|
function StoresPointers : boolean;
|
|
override;
|
|
{.Z-}
|
|
public
|
|
constructor Create(NodeClass : TStNodeClass); virtual;
|
|
{-Initialize an empty list}
|
|
|
|
procedure LoadFromStream(S : TStream); override;
|
|
{-Create a list and its data from a stream}
|
|
procedure StoreToStream(S : TStream); override;
|
|
{-Write a list and its data to a stream}
|
|
|
|
procedure Clear; override;
|
|
{-Remove all nodes from container but leave it instantiated}
|
|
|
|
function Append(Data : Pointer) : TStListNode;
|
|
{-Add a new node to the end of a list}
|
|
function Insert(Data : Pointer) : TStListNode;
|
|
{-Insert a new node at the start of a list}
|
|
function Place(Data : Pointer; P : TStListNode) : TStListNode;
|
|
{-Place a new node into a list after an existing node P}
|
|
function PlaceBefore(Data : Pointer; P : TStListNode) : TStListNode;
|
|
{-Place a new node into a list before an existing node P}
|
|
function InsertSorted(Data : Pointer) : TStListNode;
|
|
{-Insert a new node in sorted order}
|
|
procedure MoveToHead(P : TStListNode);
|
|
{-Move P to the head of the list}
|
|
|
|
procedure Assign(Source: TPersistent); override;
|
|
{-Assign another container's contents to this one}
|
|
procedure Join(P : TStListNode; L : TStList);
|
|
{-Join list L after P in the current list. L is freed}
|
|
function Split(P : TStListNode) : TStList;
|
|
{-Split list, creating a new list that starts with P}
|
|
|
|
procedure Sort;
|
|
{-Put the list into sorted order}
|
|
|
|
procedure Delete(P : TStListNode);
|
|
{-Remove an element and dispose of its contents}
|
|
|
|
function Next(P : TStListNode) : TStListNode;
|
|
{-Return the node after P, nil if none}
|
|
function Prev(P : TStListNode) : TStListNode;
|
|
{-Return the node before P, nil if none}
|
|
function Nth(Index : LongInt) : TStListNode;
|
|
{-Return the Index'th node in the list, Index >= 0 (cached)}
|
|
function NthFrom(P : TStListNode; Index : LongInt) : TStListNode;
|
|
{-Return the Index'th node from P, either direction}
|
|
function Posn(P : TStListNode) : LongInt;
|
|
{-Return the ordinal position of an element in the list}
|
|
function Distance(P1, P2 : TStListNode) : LongInt;
|
|
{-Return the number of nodes separating P1 and P2 (signed)}
|
|
function Find(Data : Pointer) : TStListNode;
|
|
{-Return the first node whose data equals Data}
|
|
function Iterate(Action : TIterateFunc; Up : Boolean;
|
|
OtherData : Pointer) : TStListNode;
|
|
{-Call Action for all the nodes, returning the last node visited}
|
|
|
|
property Head : TStListNode
|
|
{-Return the head node}
|
|
read FHead;
|
|
property Tail : TStListNode
|
|
{-Return the tail node}
|
|
read FTail;
|
|
property Items[Index : LongInt] : TStListNode
|
|
{-Return the Index'th node, 0-based}
|
|
read Nth;
|
|
default;
|
|
end;
|
|
|
|
{.Z+}
|
|
TStListClass = class of TStList;
|
|
{.Z-}
|
|
|
|
{======================================================================}
|
|
|
|
implementation
|
|
|
|
{$IFDEF ThreadSafe}
|
|
var
|
|
ClassCritSect : TRTLCriticalSection;
|
|
{$ENDIF}
|
|
|
|
procedure EnterClassCS;
|
|
begin
|
|
{$IFDEF ThreadSafe}
|
|
EnterCriticalSection(ClassCritSect);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure LeaveClassCS;
|
|
begin
|
|
{$IFDEF ThreadSafe}
|
|
LeaveCriticalSection(ClassCritSect);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
constructor TStListNode.Create(AData : Pointer);
|
|
begin
|
|
inherited Create(AData);
|
|
end;
|
|
|
|
{----------------------------------------------------------------------}
|
|
|
|
function FindNode(Container : TStContainer;
|
|
Node : TStNode;
|
|
OtherData : Pointer) : Boolean; far;
|
|
begin
|
|
Result := (Node.Data <> OtherData);
|
|
end;
|
|
|
|
function AssignData(Container : TStContainer;
|
|
Data, OtherData : Pointer) : Boolean; far;
|
|
var
|
|
OurList : TStList absolute OtherData;
|
|
begin
|
|
OurList.Append(Data);
|
|
Result := true;
|
|
end;
|
|
|
|
{----------------------------------------------------------------------}
|
|
|
|
function TStList.Append(Data : Pointer) : TStListNode;
|
|
var
|
|
N : TStListNode;
|
|
begin
|
|
{$IFDEF ThreadSafe}
|
|
EnterCS;
|
|
try
|
|
{$ENDIF}
|
|
N := TStListNode(conNodeClass.Create(Data));
|
|
N.FPrev := FTail;
|
|
if not Assigned(FHead) then begin
|
|
{Special case for first node}
|
|
FHead := N;
|
|
FTail := N;
|
|
end else begin
|
|
{Add at end of existing list}
|
|
FTail.FNext := N;
|
|
FTail := N;
|
|
end;
|
|
Inc(FCount);
|
|
Result := N;
|
|
{$IFDEF ThreadSafe}
|
|
finally
|
|
LeaveCS;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TStList.Assign(Source: TPersistent);
|
|
begin
|
|
{$IFDEF ThreadSafe}
|
|
EnterCS;
|
|
try
|
|
{$ENDIF}
|
|
{The only containers that we allow to be assigned to a linked list are
|
|
- another SysTools linked list (TStList)
|
|
- a SysTools binary search tree (TStTree)
|
|
- a SysTools collection (TStCollection, TStSortedCollection)}
|
|
if not AssignPointers(Source, AssignData) then
|
|
inherited Assign(Source);
|
|
{$IFDEF ThreadSafe}
|
|
finally
|
|
LeaveCS;
|
|
end;{try..finally}
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TStList.Clear;
|
|
begin
|
|
{$IFDEF ThreadSafe}
|
|
EnterCS;
|
|
try
|
|
{$ENDIF}
|
|
if Count > 0 then begin
|
|
Iterate(DestroyNode, True, nil);
|
|
FCount := 0;
|
|
end;
|
|
FHead := nil;
|
|
FTail := nil;
|
|
lsLastI := -1;
|
|
lsLastP := nil;
|
|
{$IFDEF ThreadSafe}
|
|
finally
|
|
LeaveCS;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
constructor TStList.Create(NodeClass : TStNodeClass);
|
|
begin
|
|
CreateContainer(NodeClass, 0);
|
|
Clear;
|
|
end;
|
|
|
|
procedure TStList.Delete(P : TStListNode);
|
|
begin
|
|
{$IFDEF ThreadSafe}
|
|
EnterCS;
|
|
try
|
|
{$ENDIF}
|
|
if (not Assigned(P)) or (Count <= 0) then
|
|
Exit;
|
|
if not (P is conNodeClass) then
|
|
RaiseContainerError(stscBadType);
|
|
|
|
with P do begin
|
|
{Fix pointers of surrounding nodes}
|
|
if Assigned(FNext) then
|
|
FNext.FPrev := FPrev;
|
|
if Assigned(FPrev) then
|
|
FPrev.FNext := FNext;
|
|
end;
|
|
|
|
{Fix head and tail of list}
|
|
if FTail = P then
|
|
FTail := FTail.FPrev;
|
|
if FHead = P then
|
|
FHead := FHead.FNext;
|
|
|
|
{Dispose of the node}
|
|
DisposeNodeData(P);
|
|
P.Free;
|
|
Dec(FCount);
|
|
lsLastI := -1;
|
|
{$IFDEF ThreadSafe}
|
|
finally
|
|
LeaveCS;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TStList.Distance(P1, P2 : TStListNode) : LongInt;
|
|
var
|
|
I : LongInt;
|
|
N : TStListNode;
|
|
begin
|
|
{$IFDEF ThreadSafe}
|
|
EnterCS;
|
|
try
|
|
{$ENDIF}
|
|
{Count forward}
|
|
I := 0;
|
|
N := P1;
|
|
while Assigned(N) and (N <> P2) do begin
|
|
Inc(I);
|
|
N := N.FNext;
|
|
end;
|
|
if N = P2 then begin
|
|
Result := I;
|
|
Exit;
|
|
end;
|
|
|
|
{Count backward}
|
|
I := 0;
|
|
N := P1;
|
|
while Assigned(N) and (N <> P2) do begin
|
|
Dec(I);
|
|
N := N.FPrev;
|
|
end;
|
|
if N = P2 then begin
|
|
Result := I;
|
|
Exit;
|
|
end;
|
|
|
|
{Not on same list}
|
|
Result := MaxLongInt;
|
|
{$IFDEF ThreadSafe}
|
|
finally
|
|
LeaveCS;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TStList.Find(Data : Pointer) : TStListNode;
|
|
begin
|
|
{$IFDEF ThreadSafe}
|
|
EnterCS;
|
|
try
|
|
{$ENDIF}
|
|
Result := Iterate(FindNode, True, Data);
|
|
{$IFDEF ThreadSafe}
|
|
finally
|
|
LeaveCS;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TStList.ForEachPointer(Action : TIteratePointerFunc;
|
|
OtherData : pointer);
|
|
var
|
|
N : TStListNode;
|
|
P : TStListNode;
|
|
begin
|
|
{$IFDEF ThreadSafe}
|
|
EnterCS;
|
|
try
|
|
{$ENDIF}
|
|
N := FHead;
|
|
while Assigned(N) do begin
|
|
P := N.FNext;
|
|
if Action(Self, N.Data, OtherData) then
|
|
N := P
|
|
else
|
|
Exit;
|
|
end;
|
|
{$IFDEF ThreadSafe}
|
|
finally
|
|
LeaveCS;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TStList.Insert(Data : Pointer) : TStListNode;
|
|
var
|
|
N : TStListNode;
|
|
begin
|
|
{$IFDEF ThreadSafe}
|
|
EnterCS;
|
|
try
|
|
{$ENDIF}
|
|
N := TStListNode(conNodeClass.Create(Data));
|
|
{N.FPrev := nil;}
|
|
N.FNext := FHead;
|
|
if not Assigned(FHead) then
|
|
{Special case for first node}
|
|
FTail := N
|
|
else
|
|
{Add at start of existing list}
|
|
FHead.FPrev := N;
|
|
FHead := N;
|
|
Inc(FCount);
|
|
lsLastI := -1;
|
|
Result := N;
|
|
{$IFDEF ThreadSafe}
|
|
finally
|
|
LeaveCS;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TStList.InsertSorted(Data : Pointer) : TStListNode;
|
|
var
|
|
N : TStListNode;
|
|
P : TStListNode;
|
|
begin
|
|
{$IFDEF ThreadSafe}
|
|
EnterCS;
|
|
try
|
|
{$ENDIF}
|
|
N := TStListNode(conNodeClass.Create(Data));
|
|
Result := N;
|
|
Inc(FCount);
|
|
lsLastI := -1;
|
|
|
|
if not Assigned(FHead) then begin
|
|
{First element added to list}
|
|
FHead := N;
|
|
FTail := N;
|
|
end else begin
|
|
P := FHead;
|
|
while Assigned(P) do begin
|
|
if DoCompare(N.Data, P.Data) < 0 then begin
|
|
if not Assigned(P.FPrev) then begin
|
|
{New head}
|
|
FHead := N;
|
|
end else begin
|
|
P.FPrev.FNext := N;
|
|
N.FPrev := P.FPrev;
|
|
end;
|
|
P.FPrev := N;
|
|
N.FNext := P;
|
|
Exit;
|
|
end;
|
|
P := P.FNext;
|
|
end;
|
|
{New tail}
|
|
FTail.FNext := N;
|
|
N.FPrev := FTail;
|
|
FTail := N;
|
|
end;
|
|
{$IFDEF ThreadSafe}
|
|
finally
|
|
LeaveCS;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TStList.Iterate(Action : TIterateFunc; Up : Boolean;
|
|
OtherData : Pointer) : TStListNode;
|
|
var
|
|
N : TStListNode;
|
|
P : TStListNode;
|
|
begin
|
|
{$IFDEF ThreadSafe}
|
|
EnterCS;
|
|
try
|
|
{$ENDIF}
|
|
if Up then begin
|
|
N := FHead;
|
|
while Assigned(N) do begin
|
|
P := N.FNext;
|
|
if Action(Self, N, OtherData) then
|
|
N := P
|
|
else begin
|
|
Result := N;
|
|
Exit;
|
|
end;
|
|
end;
|
|
end else begin
|
|
N := FTail;
|
|
while Assigned(N) do begin
|
|
P := N.FPrev;
|
|
if Action(Self, N, OtherData) then
|
|
N := P
|
|
else begin
|
|
Result := N;
|
|
Exit;
|
|
end;
|
|
end;
|
|
end;
|
|
Result := nil;
|
|
{$IFDEF ThreadSafe}
|
|
finally
|
|
LeaveCS;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TStList.Join(P : TStListNode; L : TStList);
|
|
var
|
|
N : TStListNode;
|
|
Q : TStListNode;
|
|
begin
|
|
{$IFDEF ThreadSafe}
|
|
EnterClassCS;
|
|
EnterCS;
|
|
L.EnterCS;
|
|
try
|
|
{$ENDIF}
|
|
if Assigned(L) then begin
|
|
if Assigned(P) and (L.Count > 0) then begin
|
|
{Patch the list into the current one}
|
|
N := L.Head;
|
|
Q := P.FNext;
|
|
|
|
P.FNext := N;
|
|
N.FPrev := P;
|
|
|
|
if Assigned(Q) then begin
|
|
N := L.Tail;
|
|
N.FNext := Q;
|
|
Q.FPrev := N;
|
|
end;
|
|
|
|
Inc(FCount, L.Count);
|
|
lsLastI := -1;
|
|
end;
|
|
|
|
{Free L (but not its nodes)}
|
|
L.IncNodeProtection;
|
|
L.Free;
|
|
end;
|
|
{$IFDEF ThreadSafe}
|
|
finally
|
|
L.LeaveCS;
|
|
LeaveCS;
|
|
LeaveClassCS;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TStList.LoadFromStream(S : TStream);
|
|
var
|
|
Data : pointer;
|
|
Reader : TReader;
|
|
StreamedClass : TPersistentClass;
|
|
StreamedNodeClass : TPersistentClass;
|
|
StreamedClassName : string;
|
|
StreamedNodeClassName : string;
|
|
begin
|
|
{$IFDEF ThreadSafe}
|
|
EnterCS;
|
|
try
|
|
{$ENDIF}
|
|
Clear;
|
|
Reader := TReader.Create(S, 1024);
|
|
try
|
|
with Reader do
|
|
begin
|
|
StreamedClassName := ReadString;
|
|
StreamedClass := GetClass(StreamedClassName);
|
|
if (StreamedClass = nil) then
|
|
RaiseContainerErrorFmt(stscUnknownClass, [StreamedClassName]);
|
|
if (not IsOrInheritsFrom(StreamedClass, Self.ClassType)) or
|
|
(not IsOrInheritsFrom(TStList, StreamedClass)) then
|
|
RaiseContainerError(stscWrongClass);
|
|
StreamedNodeClassName := ReadString;
|
|
StreamedNodeClass := GetClass(StreamedNodeClassName);
|
|
if (StreamedNodeClass = nil) then
|
|
RaiseContainerErrorFmt(stscUnknownNodeClass, [StreamedNodeClassName]);
|
|
if (not IsOrInheritsFrom(StreamedNodeClass, conNodeClass)) or
|
|
(not IsOrInheritsFrom(TStListNode, StreamedNodeClass)) then
|
|
RaiseContainerError(stscWrongNodeClass);
|
|
ReadListBegin;
|
|
while not EndOfList do
|
|
begin
|
|
Data := DoLoadData(Reader);
|
|
Append(Data);
|
|
end;
|
|
ReadListEnd;
|
|
end;
|
|
finally
|
|
Reader.Free;
|
|
end;
|
|
{$IFDEF ThreadSafe}
|
|
finally
|
|
LeaveCS;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TStList.MoveToHead(P : TStListNode);
|
|
begin
|
|
{$IFDEF ThreadSafe}
|
|
EnterCS;
|
|
try
|
|
{$ENDIF}
|
|
if Assigned(P) then
|
|
if P <> Head then begin
|
|
with P do begin
|
|
{Fix pointers of surrounding nodes}
|
|
if FTail = P then
|
|
FTail := FTail.FPrev
|
|
else
|
|
FNext.FPrev := FPrev;
|
|
FPrev.FNext := FNext;
|
|
|
|
FNext := FHead;
|
|
FPrev := nil;
|
|
end;
|
|
FHead.FPrev := P;
|
|
FHead := P;
|
|
end;
|
|
{$IFDEF ThreadSafe}
|
|
finally
|
|
LeaveCS;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TStList.Next(P : TStListNode) : TStListNode;
|
|
begin
|
|
{$IFDEF ThreadSafe}
|
|
EnterCS;
|
|
try
|
|
{$ENDIF}
|
|
Result := P.FNext;
|
|
{$IFDEF ThreadSafe}
|
|
finally
|
|
LeaveCS;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TStList.Nth(Index : LongInt) : TStListNode;
|
|
var
|
|
MinI : LongInt;
|
|
MinP : TStListNode;
|
|
begin
|
|
{$IFDEF ThreadSafe}
|
|
EnterCS;
|
|
try
|
|
{$ENDIF}
|
|
if (Index < 0) or (Index >= FCount) then
|
|
Result := nil
|
|
else begin
|
|
MinI := Index;
|
|
MinP := FHead;
|
|
if lsLastI >= 0 then
|
|
{scan the fewest possible nodes}
|
|
if Index <= lsLastI then begin
|
|
if lsLastI-Index < Index then begin
|
|
MinI := Index-lsLastI;
|
|
MinP := lsLastP;
|
|
end;
|
|
end else if Index-lsLastI < FCount-1-Index then begin
|
|
MinI := Index-lsLastI;
|
|
MinP := lsLastP;
|
|
end else begin
|
|
MinI := Index-(FCount-1);
|
|
MinP := FTail;
|
|
end;
|
|
|
|
Result := NthFrom(MinP, MinI);
|
|
lsLastI := Index;
|
|
lsLastP := Result;
|
|
end;
|
|
{$IFDEF ThreadSafe}
|
|
finally
|
|
LeaveCS;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TStList.NthFrom(P : TStListNode; Index : LongInt) : TStListNode;
|
|
var
|
|
I : LongInt;
|
|
begin
|
|
{$IFDEF ThreadSafe}
|
|
EnterCS;
|
|
try
|
|
{$ENDIF}
|
|
if Assigned(P) then begin
|
|
if not (P is conNodeClass) then
|
|
RaiseContainerError(stscBadType);
|
|
if Index > 0 then begin
|
|
for I := 1 to Index do begin
|
|
P := P.FNext;
|
|
if not Assigned(P) then
|
|
break;
|
|
end;
|
|
end else begin
|
|
for I := 1 to -Index do begin
|
|
P := P.FPrev;
|
|
if not Assigned(P) then
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
Result := P;
|
|
{$IFDEF ThreadSafe}
|
|
finally
|
|
LeaveCS;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TStList.Place(Data : Pointer; P : TStListNode) : TStListNode;
|
|
var
|
|
N : TStListNode;
|
|
begin
|
|
{$IFDEF ThreadSafe}
|
|
EnterCS;
|
|
try
|
|
{$ENDIF}
|
|
if not Assigned(P) then
|
|
Result := Insert(Data)
|
|
else if P = FTail then
|
|
Result := Append(Data)
|
|
else begin
|
|
N := TStListNode(conNodeClass.Create(Data));
|
|
N.FPrev := P;
|
|
N.FNext := P.FNext;
|
|
P.FNext.FPrev := N;
|
|
P.FNext := N;
|
|
Inc(FCount);
|
|
lsLastI := -1;
|
|
Result := N;
|
|
end;
|
|
{$IFDEF ThreadSafe}
|
|
finally
|
|
LeaveCS;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TStList.PlaceBefore(Data : Pointer; P : TStListNode) : TStListNode;
|
|
var
|
|
N : TStListNode;
|
|
begin
|
|
{$IFDEF ThreadSafe}
|
|
EnterCS;
|
|
try
|
|
{$ENDIF}
|
|
if (not Assigned(P)) or (P = Head) then
|
|
{Place the new element at the start of the list}
|
|
Result := Insert(Data)
|
|
else begin
|
|
{Patch in the new element}
|
|
N := TStListNode(conNodeClass.Create(Data));
|
|
N.FNext := P;
|
|
N.FPrev := P.FPrev;
|
|
P.FPrev.FNext := N;
|
|
P.FPrev := N;
|
|
lsLastI := -1;
|
|
Inc(FCount);
|
|
Result := N;
|
|
end;
|
|
{$IFDEF ThreadSafe}
|
|
finally
|
|
LeaveCS;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TStList.Posn(P : TStListNode) : LongInt;
|
|
var
|
|
I : LongInt;
|
|
N : TStListNode;
|
|
begin
|
|
{$IFDEF ThreadSafe}
|
|
EnterCS;
|
|
try
|
|
{$ENDIF}
|
|
if not Assigned(P) then
|
|
Result := -1
|
|
else begin
|
|
if not (P is conNodeClass) then
|
|
RaiseContainerError(stscBadType);
|
|
I := 0;
|
|
N := FHead;
|
|
while Assigned(N) do begin
|
|
if P = N then begin
|
|
Result := I;
|
|
exit;
|
|
end;
|
|
Inc(I);
|
|
N := N.FNext;
|
|
end;
|
|
Result := -1;
|
|
end;
|
|
{$IFDEF ThreadSafe}
|
|
finally
|
|
LeaveCS;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TStList.Prev(P : TStListNode) : TStListNode;
|
|
begin
|
|
{$IFDEF ThreadSafe}
|
|
EnterCS;
|
|
try
|
|
{$ENDIF}
|
|
Result := P.FPrev;
|
|
{$IFDEF ThreadSafe}
|
|
finally
|
|
LeaveCS;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TStList.Sort;
|
|
const
|
|
StackSize = 32;
|
|
type
|
|
Stack = array[0..StackSize-1] of TStListNode;
|
|
var
|
|
L : TStListNode;
|
|
R : TStListNode;
|
|
PL : TStListNode;
|
|
PR : TStListNode;
|
|
PivotData : Pointer;
|
|
TmpData : Pointer;
|
|
Dist : LongInt;
|
|
DistL : LongInt;
|
|
DistR : LongInt;
|
|
StackP : Integer;
|
|
LStack : Stack;
|
|
RStack : Stack;
|
|
DStack : array[0..StackSize-1] of LongInt;
|
|
begin
|
|
{$IFDEF ThreadSafe}
|
|
EnterCS;
|
|
try
|
|
{$ENDIF}
|
|
{Need at least 2 elements to sort}
|
|
if Count <= 1 then
|
|
Exit;
|
|
lsLastI := -1;
|
|
|
|
{Initialize the stacks}
|
|
StackP := 0;
|
|
LStack[0] := FHead;
|
|
RStack[0] := FTail;
|
|
DStack[0] := Count-1;
|
|
|
|
{Repeatedly take top partition from stack}
|
|
repeat
|
|
|
|
{Pop the stack}
|
|
L := LStack[StackP];
|
|
R := RStack[StackP];
|
|
Dist := DStack[StackP];
|
|
Dec(StackP);
|
|
|
|
if L <> R then
|
|
{Sort current partition}
|
|
repeat
|
|
|
|
{Load the pivot element}
|
|
PivotData := NthFrom(L, Dist div 2).Data;
|
|
PL := L;
|
|
PR := R;
|
|
DistL := Dist;
|
|
DistR := Dist;
|
|
|
|
{Swap items in sort order around the pivot index}
|
|
repeat
|
|
while DoCompare(PL.Data, PivotData) < 0 do begin
|
|
PL := PL.FNext;
|
|
Dec(Dist);
|
|
Dec(DistR);
|
|
end;
|
|
while DoCompare(PivotData, PR.Data) < 0 do begin
|
|
PR := PR.FPrev;
|
|
Dec(Dist);
|
|
Dec(DistL);
|
|
end;
|
|
if Dist >= 0 then begin
|
|
if PL <> PR then begin
|
|
{Swap the two elements}
|
|
TmpData := PL.Data;
|
|
PL.Data := PR.Data;
|
|
PR.Data := TmpData;
|
|
end;
|
|
if Assigned(PL.FNext) then begin
|
|
PL := PL.FNext;
|
|
Dec(Dist);
|
|
Dec(DistR);
|
|
end;
|
|
if Assigned(PR.FPrev) then begin
|
|
PR := PR.FPrev;
|
|
Dec(Dist);
|
|
Dec(DistL);
|
|
end;
|
|
end;
|
|
until Dist < 0;
|
|
|
|
{Decide which partition to sort next}
|
|
if DistL < DistR then begin
|
|
{Right partition is bigger}
|
|
if DistR > 0 then begin
|
|
{Stack the request for sorting right partition}
|
|
Inc(StackP);
|
|
LStack[StackP] := PL;
|
|
RStack[StackP] := R;
|
|
DStack[StackP] := DistR;
|
|
end;
|
|
{Continue sorting left partition}
|
|
R := PR;
|
|
Dist := DistL;
|
|
end else begin
|
|
{Left partition is bigger}
|
|
if DistL > 0 then begin
|
|
{Stack the request for sorting left partition}
|
|
Inc(StackP);
|
|
LStack[StackP] := L;
|
|
RStack[StackP] := PR;
|
|
DStack[StackP] := DistL;
|
|
end;
|
|
{Continue sorting right partition}
|
|
L := PL;
|
|
Dist := DistR;
|
|
end;
|
|
|
|
until Dist <= 0;
|
|
until StackP < 0;
|
|
{$IFDEF ThreadSafe}
|
|
finally
|
|
LeaveCS;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TStList.Split(P : TStListNode) : TStList;
|
|
var
|
|
I : LongInt;
|
|
begin
|
|
{$IFDEF ThreadSafe}
|
|
EnterCS;
|
|
try
|
|
{$ENDIF}
|
|
I := Posn(P);
|
|
if I < 0 then begin
|
|
Result := nil;
|
|
Exit;
|
|
end;
|
|
|
|
{Create and initialize the new list}
|
|
Result := TStListClass(ClassType).Create(conNodeClass);
|
|
Result.Compare := Compare;
|
|
Result.OnCompare := OnCompare;
|
|
Result.DisposeData := DisposeData;
|
|
Result.OnDisposeData := OnDisposeData;
|
|
Result.LoadData := LoadData;
|
|
Result.OnLoadData := OnLoadData;
|
|
Result.StoreData := StoreData;
|
|
Result.OnStoreData := OnStoreData;
|
|
Result.FHead := P;
|
|
Result.FTail := FTail;
|
|
Result.FCount := Count-I;
|
|
Result.lsLastI := -1;
|
|
|
|
{Truncate the old list}
|
|
if Assigned(P.FPrev) then begin
|
|
P.FPrev.FNext := nil;
|
|
FTail := P.FPrev;
|
|
P.FPrev := nil;
|
|
end;
|
|
if P = FHead then
|
|
FHead := nil;
|
|
FCount := I;
|
|
lsLastI := -1;
|
|
{$IFDEF ThreadSafe}
|
|
finally
|
|
LeaveCS;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TStList.StoresPointers : Boolean;
|
|
begin
|
|
Result := true;
|
|
end;
|
|
|
|
procedure TStList.StoreToStream(S : TStream);
|
|
var
|
|
Writer : TWriter;
|
|
Walker : TStListNode;
|
|
begin
|
|
{$IFDEF ThreadSafe}
|
|
EnterCS;
|
|
try
|
|
{$ENDIF}
|
|
Writer := TWriter.Create(S, 1024);
|
|
try
|
|
with Writer do
|
|
begin
|
|
WriteString(Self.ClassName);
|
|
WriteString(conNodeClass.ClassName);
|
|
WriteListBegin;
|
|
Walker := Head;
|
|
while Walker <> nil do
|
|
begin
|
|
DoStoreData(Writer, Walker.Data);
|
|
Walker := Next(Walker);
|
|
end;
|
|
WriteListEnd;
|
|
end;
|
|
finally
|
|
Writer.Free;
|
|
end;
|
|
{$IFDEF ThreadSafe}
|
|
finally
|
|
LeaveCS;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{$IFDEF ThreadSafe}
|
|
initialization
|
|
Windows.InitializeCriticalSection(ClassCritSect);
|
|
finalization
|
|
Windows.DeleteCriticalSection(ClassCritSect);
|
|
{$ENDIF}
|
|
end.
|