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.