unit LinkBuf;
interface
type
PLinkNode=^TLinkNode;
TLinkNode=record
DataLen:Integer;
LastNode,NextNode:PLinkNode;
end;
TLinkBuf=class
private
FHeadNode,FTailNode,FLastNodeHasData:PLinkNode;
FNodeSize:Integer;
FContentSize:Integer;
FCapacity: Integer;
FNodeCount: Integer;
FTailEmptyNodeCount: Integer;
function GetAsString: String;
function Enlarge(AddLen:Integer):Boolean;
function GetTailSpace:Integer;
public
property ContentSize:Integer read FContentSize;
property Capacity:Integer read FCapacity;
property NodeSize:Integer read FNodeSize;
property NodeCount:Integer read FNodeCount;
property TailSpace:Integer read GetTailSpace;
property AsString:String read GetAsString;
function AppendData(P:PChar;Len:Integer):Boolean;
procedure ClearBuf(FreeMemory:Boolean=true);
constructor Create(NodeSize:Integer=32768);
destructor Destroy; override;
end;
const
StructHeadLen=SizeOf(TLinkNode);
implementation
{ TBuffer }
procedure TLinkBuf.ClearBuf(FreeMemory: Boolean);
var
TempNode,mNode:PLinkNode;
begin
TempNode:=FHeadNode;
if FreeMemory then
begin
while TempNode<>nil do
begin
mNode:=TempNode;
TempNode:=TempNode.NextNode;
FreeMem(mNode);
end;
FHeadNode:=nil;
FTailNode:=nil;
FLastNodeHasData:=nil;
FCapacity:=0;
FNodeCount:=0;
end
else begin
while TempNode<>nil do
begin
TempNode.DataLen:=0;
TempNode:=TempNode.NextNode;
end;
FLastNodeHasData:=FHeadNode;
FTailEmptyNodeCount:=FNodeCount;
end;
FContentSize:=0;
end;
constructor TLinkBuf.Create(NodeSize: Integer);
begin
FHeadNode:=nil;
FTailNode:=nil;
FLastNodeHasData:=nil;
if NodeSize>0 then
FNodeSize:=NodeSize
else
FNodeSize:=32768;
FContentSize:=0;
FCapacity:=0;
FNodeCount:=0;
FTailEmptyNodeCount:=0;
end;
destructor TLinkBuf.Destroy;
begin
ClearBuf(true);
inherited;
end;
function TLinkBuf.Enlarge(AddLen: Integer): Boolean;
var
P:PChar;
TempNode:PLinkNode;
i,NewNodeCount:Integer;
begin
Result:=true;
if (AddLen<=0) or (AddLen<GetTailSpace) then //无需新增节点即可容纳新增内容
exit;
Dec(AddLen,GetTailSpace);
NewNodeCount:=(AddLen+FNodeSize-1) div FNodeSize;
for i:=1 to NewNodeCount do
begin
//感谢Barton兄提供的结构体-缓冲合并的思路 :)
try
GetMem(P,FNodeSize+StructHeadLen); //结构体和缓冲公用内存区
except
Result:=false;
exit;
end;
TempNode:=PLinkNode(P);
TempNode.DataLen:=0;
TempNode.LastNode:=FTailNode;
TempNode.NextNode:=nil;
if FTailNode=nil then
begin
FHeadNode:=TempNode;
FLastNodeHasData:=TempNode;
end
else
FTailNode.NextNode:=TempNode;
FTailNode:=TempNode;
Inc(FTailEmptyNodeCount);
Inc(FCapacity,FNodeSize);
Inc(FNodeCount);
end;
end;
function TLinkBuf.GetAsString: String;
var
TempNode:PLinkNode;
pSrc,pDest:PChar;
begin
SetLength(Result,FContentSize);
pDest:=@Result[1];
TempNode:=FHeadNode;
while TempNode<>nil do
begin
pSrc:=PChar(Integer(TempNode)+StructHeadLen);
Move(pSrc^,pDest^,TempNode.DataLen);
if TempNode=FLastNodeHasData then
break;
Inc(pDest,TempNode.DataLen);
TempNode:=TempNode.NextNode;
end;
end;
function TLinkBuf.AppendData(P: PChar; Len: Integer): Boolean;
var
PC:PChar;
TempNode:PLinkNode;
MoveLen:Integer;
begin
if GetTailSpace<Len then
Result:=Enlarge(Len)
else
Result:=true;
if Result then
begin
TempNode:=FLastNodeHasData;
while Len>0 do
begin
with TempNode^ do
begin
PC:=PChar(Integer(TempNode)+StructHeadLen+DataLen);
MoveLen:=FNodeSize-DataLen;
if DataLen=0 then //is empty node
Dec(FTailEmptyNodeCount);
end;
if MoveLen>Len then
MoveLen:=Len;
Move(P^,PC^,MoveLen);
Inc(TempNode.DataLen,MoveLen);
Dec(Len,MoveLen);
Inc(P,MoveLen);
Inc(FContentSize,MoveLen);
FLastNodeHasData:=TempNode;
TempNode:=TempNode.NextNode;
end;
end;
end;
function TLinkBuf.GetTailSpace: Integer;
begin
if FLastNodeHasData=nil then
Result:=0
else
Result:=FNodeSize-FLastNodeHasData.DataLen+FNodeSize*FTailEmptyNodeCount;
end;
end.