|
@@ -14,6 +14,11 @@
|
|
|
|
|
|
unit gdeque;
|
|
|
|
|
|
+{
|
|
|
+ Implements a generic double ended queue.
|
|
|
+ (See: https://en.wikipedia.org/wiki/Double-ended_queue)
|
|
|
+}
|
|
|
+
|
|
|
interface
|
|
|
|
|
|
type
|
|
@@ -30,10 +35,18 @@ type
|
|
|
procedure SetValue(position:SizeUInt; value:T);inline;
|
|
|
function GetValue(position:SizeUInt):T;inline;
|
|
|
function GetMutable(position:SizeUInt):PT;inline;
|
|
|
- procedure IncreaseCapacity();inline;
|
|
|
+ procedure IncreaseCapacity();
|
|
|
+ protected
|
|
|
+ procedure MoveSimpleData(StartIndex: SizeUInt; Offset: SizeInt; NrElems: SizeUInt);
|
|
|
+ procedure MoveManagedData(StartIndex: SizeUInt; Offset: SizeInt; NrElems: SizeUInt);
|
|
|
+ procedure MoveData(StartIndex: SizeUInt; Offset: SizeInt; NrElems: SizeUInt);
|
|
|
+ procedure ClearSingleDataEntry(Index: SizeUInt); virtual;
|
|
|
+ procedure ClearData; virtual;
|
|
|
+ property Data: TArr read FData;
|
|
|
public
|
|
|
function Size():SizeUInt;inline;
|
|
|
constructor Create();
|
|
|
+ destructor Destroy(); override;
|
|
|
Procedure Clear;
|
|
|
procedure PushBack(value:T);inline;
|
|
|
procedure PushFront(value:T);inline;
|
|
@@ -59,8 +72,15 @@ begin
|
|
|
FStart:=0;
|
|
|
end;
|
|
|
|
|
|
+destructor TDeque.Destroy();
|
|
|
+begin
|
|
|
+ Clear;
|
|
|
+ inherited Destroy;
|
|
|
+end;
|
|
|
+
|
|
|
procedure TDeque.Clear;
|
|
|
begin
|
|
|
+ ClearData;
|
|
|
FDataSize:=0;
|
|
|
FStart:=0;
|
|
|
end;
|
|
@@ -87,6 +107,7 @@ procedure TDeque.PopFront();inline;
|
|
|
begin
|
|
|
if(FDataSize>0) then
|
|
|
begin
|
|
|
+ ClearSingleDataEntry(FStart);
|
|
|
inc(FStart);
|
|
|
dec(FDataSize);
|
|
|
if(FStart=FCapacity) then
|
|
@@ -97,7 +118,10 @@ end;
|
|
|
procedure TDeque.PopBack();inline;
|
|
|
begin
|
|
|
if(FDataSize>0) then
|
|
|
+ begin
|
|
|
+ ClearSingleDataEntry((FStart+FDataSize-1)mod FCapacity);
|
|
|
dec(FDataSize);
|
|
|
+ end;
|
|
|
end;
|
|
|
|
|
|
procedure TDeque.PushFront(value:T);inline;
|
|
@@ -127,6 +151,7 @@ end;
|
|
|
procedure TDeque.SetValue(position:SizeUInt; value:T);inline;
|
|
|
begin
|
|
|
Assert(position < size, 'Deque access out of range');
|
|
|
+ ClearSingleDataEntry((FStart+position)mod FCapacity);
|
|
|
FData[(FStart+position)mod FCapacity]:=value;
|
|
|
end;
|
|
|
|
|
@@ -142,7 +167,68 @@ begin
|
|
|
GetMutable:=@FData[(FStart+position) mod FCapacity];
|
|
|
end;
|
|
|
|
|
|
-procedure TDeque.IncreaseCapacity;inline;
|
|
|
+
|
|
|
+procedure TDeque.MoveSimpleData(StartIndex: SizeUInt; Offset: SizeInt; NrElems: SizeUInt);
|
|
|
+begin
|
|
|
+ Move(FData[StartIndex], FData[StartIndex+Offset], NrElems*SizeOf(T));
|
|
|
+ if Offset>0 then
|
|
|
+ FillChar(FData[StartIndex], NrElems*SizeOf(T), 0)
|
|
|
+ else
|
|
|
+ FillChar(FData[StartIndex+NrElems+Offset], -Offset*SizeOf(T), 0);
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TDeque.MoveManagedData(StartIndex: SizeUInt; Offset: SizeInt; NrElems: SizeUInt);
|
|
|
+var
|
|
|
+ i: SizeUInt;
|
|
|
+begin
|
|
|
+ //since we always move blocks where Abs(Offset)>=NrElems, there is no need for
|
|
|
+ //2 seperate loops (1 for ngeative and 1 for positive Offsett)
|
|
|
+ for i := 0 to NrElems-1 do
|
|
|
+ begin
|
|
|
+ Finalize(FData[StartIndex+i+Offset]);
|
|
|
+ FData[StartIndex+i+Offset] := FData[StartIndex+i];
|
|
|
+ Finalize(FData[StartIndex+i]);
|
|
|
+ FillChar(FData[StartIndex+i], SizeOf(T), 0);
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TDeque.MoveData(StartIndex: SizeUInt; Offset: SizeInt; NrElems: SizeUInt);
|
|
|
+begin
|
|
|
+ if IsManagedType(T) then
|
|
|
+ MoveManagedData(StartIndex, Offset, NrElems)
|
|
|
+ else
|
|
|
+ MoveSimpleData(StartIndex, Offset, NrElems);
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TDeque.ClearSingleDataEntry(Index: SizeUInt);
|
|
|
+begin
|
|
|
+ if IsManagedType(T) then
|
|
|
+ begin
|
|
|
+ Finalize(FData[Index]);
|
|
|
+ FillChar(FData[Index], SizeOf(T), 0);
|
|
|
+ end
|
|
|
+ else
|
|
|
+ FData[Index] := default(T);
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TDeque.ClearData;
|
|
|
+var
|
|
|
+ i: SizeUint;
|
|
|
+begin
|
|
|
+ if IsManagedType(T) then
|
|
|
+ for i := Low(FData) to High(FData) do
|
|
|
+ Finalize(FData[i]);
|
|
|
+ FillChar(FData[Low(FData)], SizeUInt(Length(FData))*SizeOf(T), 0);
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TDeque.IncreaseCapacity;
|
|
|
+ function Min(const A,B: SizeUInt): SizeUInt; inline; //no need to drag in the entire Math unit ;-)
|
|
|
+ begin
|
|
|
+ if (A<B) then
|
|
|
+ Result:=A
|
|
|
+ else
|
|
|
+ Result:=B;
|
|
|
+ end;
|
|
|
const
|
|
|
// if size is small, multiply by 2;
|
|
|
// if size bigger but <256M, inc by 1/8*size;
|
|
@@ -151,7 +237,7 @@ const
|
|
|
cSizeBig = 256*1024*1024;
|
|
|
var
|
|
|
i,OldEnd,
|
|
|
- DataSize:SizeUInt;
|
|
|
+ DataSize,CurLast,EmptyElems,Elems:SizeUInt;
|
|
|
begin
|
|
|
OldEnd:=FCapacity;
|
|
|
DataSize:=FCapacity*SizeOf(T);
|
|
@@ -165,11 +251,26 @@ begin
|
|
|
FCapacity:=FCapacity+FCapacity div 8
|
|
|
else
|
|
|
FCapacity:=FCapacity+FCapacity div 16;
|
|
|
-
|
|
|
SetLength(FData, FCapacity);
|
|
|
if (FStart>0) then
|
|
|
- for i:=0 to FStart-1 do
|
|
|
- FData[OldEnd+i]:=FData[i];
|
|
|
+ begin
|
|
|
+ if (FCapacity-OldEnd>=FStart) then //we have room to move all items in one go
|
|
|
+ begin
|
|
|
+ MoveData(0, OldEnd ,FStart)
|
|
|
+ end
|
|
|
+ else
|
|
|
+ begin //we have to move things around in chunks: we have more data in front of FStart than we have newly created unused elements
|
|
|
+ CurLast := OldEnd-1;
|
|
|
+ EmptyElems:=FCapacity-1-CurLast;
|
|
|
+ while (FStart>0) do
|
|
|
+ begin
|
|
|
+ Elems := Min(EmptyElems, FStart);
|
|
|
+ MoveData(0, CurLast+1, Elems);
|
|
|
+ MoveData(Elems, -Elems, FCapacity-Elems);
|
|
|
+ Dec(FStart, Elems);
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
end;
|
|
|
|
|
|
procedure TDeque.Reserve(cap:SizeUInt);inline;
|