Lazarus中文社区

 找回密码
 立即注册(注册审核可向QQ群索取)

QQ登录

只需一步,快速开始

版权申明
查看: 5005|回复: 0

实现环形缓冲区的通用类

[复制链接]

该用户从未签到

发表于 2010-3-13 07:28:47 | 显示全部楼层 |阅读模式
{*************************************************************************
功能描述:实现环形缓冲区的通用类,适用于流式通信模式
*************************************************************************}
{*************************************************************************
环形缓冲区通常有一个读指针和一个写指针。读指针指向环形缓冲区中可读的数据,
写指针指向环形缓冲区中可写的缓冲区。通过移动读指针和写指针就可以实现缓冲
区的数据读取和写人。在通常情况下,环形缓冲区的读用户仅仅会影响读指针,而
写用户仅仅会影响写指针。如果仅仅有一个读用户和一个写用户,那么不需要添加
互斥保护机制就可以保证数据的正确性。如果有多个读写用户访问环形缓冲区,那
么必须添加互斥保护机制来确保多个用户互斥访问环形缓冲区。
*************************************************************************}
unit u_Share_RingBuffer;

interface
    uses Windows;

const
   MAX_BUFFER_LEN = 4096;

type
   //环形缓冲区类--可变数组缓冲区类
   TRingBuffer = class(TObject)
      private
         FBuffer     : array of Char;          //缓冲区

         FBufferLen   : Longint;                //缓冲区长度
         FisWrap      : Boolean;                //是否返回头部
         FCritical    : TRTLCriticalSection;    //临界区
         FIsCritical  : Boolean;                //是否需要临界区
         procedure Require();                  //共享锁内存
         procedure Release();                  //共享解锁
         function GetEmptyCount()ongint;    //剩余空白数据量
      public
         FReadPos    : LongInt;                //读取点
         FWritePos   : LongInt;                //写入点
         function GetDataCount()ongint;                            //取得可用数据量
         function Readbuffer(var Buffer; Count: Longint)ongint;    //读取数据
         function WriteBuffer(const Buffer; Count: Longint)ongint; //写入数据
         function CopyBuffer(var Buffer; Count: Longint)ongint;    //复制数据        
         function ResizeBuffer(ABufferLenongint):Boolean;          //更改缓冲区大小
         function ReSet():Boolean; //复位缓冲区
         constructor Create(ABufferLenongint);
         destructor Destory();
      published
         property BufferLen: Longint read FBufferLen;                    //缓冲区总小小
         property IsCritical:Boolean read FIsCritical write FIsCritical; //多线程情况下需要考虑互斥访问共享缓冲区时使用
        
   end;

   //环形缓冲类--阻塞(同步)模式读写--继承自TRingBuffer
   //适合于多线程读写
   TRingBufferSyn = class(TRingBuffer)
      private
        FSynEvent : THandle;  //同步读写事件句柄
      public
        function ReadbufferSyn(var Buffer; Count: Longint;TimeOutongint):Integer;    //读取数据
        function WriteBufferSyn(const Buffer; Count: Longint;TimeOutongint):Integer; //写入数据
        constructor Create(ABufferLenongint);
        destructor Destory();
   end;

implementation

{ TRingBuffer }

constructor TRingBuffer.Create(ABufferLen:Longint);
begin
   inherited Create();
   FBufferLen := ABufferLen;
   SetLength( FBuffer,ABufferLen );
   FReadPos := 0;
   FWritePos := 0;
   FIsCritical := False;
   FisWrap  := False;                //是否返回头部
   InitializeCriticalSection(FCritical);
end;

destructor TRingBuffer.Destory;
begin
  DeleteCriticalSection(FCritical);
  inherited Destroy;
end;

procedure TRingBuffer.Release();
begin
  if FIsCritical then
  begin
    LeaveCriticalSection(FCritical);
  end;
end;

function TRingBuffer.CopyBuffer(var Buffer; Count: Longint):Longint;    //复制数据
var
  Li_RemainCount,Litemp : Integer;
  tempbuf:array[0..MAX_BUFFER_LEN] of Char;
  Datacount:Integer;
begin
   try
      Result := 0;
      Require();
      Datacount := GetDataCount();  //获取实际数据量
      if (Datacount <= 0) or ( Count = 0 ) then  //无数据可读的情况
      begin
         Result := 0;
         Exit;
      end;
      FillChar(tempbuf,MAX_BUFFER_LEN-1,0);
      if Datacount >= Count then   //有足够数据可读
      begin
         if FWritePos > FReadPos then
         begin
             Move(FBuffer[FReadPos],Buffer,Count);
             Result := Count;
             FisWrap := false;
         end else begin
            //先判断尾部剩余空间有多少可读数据
            Li_RemainCount := FBufferLen - FReadPos;  //到缓冲区尾部还剩的缓冲量
            if Count > Li_RemainCount then            //尾部剩余数据都可读,把尾部数据全部读出
            begin
                Move(FBuffer[FReadPos],tempbuf,Li_RemainCount);          //暂存数据到临时buf
                Litemp := Count - Li_RemainCount;                        //剩余要读的数据
                Move(FBuffer[0],tempbuf[Li_RemainCount],Litemp);         //从头部读取剩余数据
                Move(tempbuf,Buffer,Count);
                Result := Count;
                FisWrap := false;
            end else begin //当可用数据不到尾部,则直接读取
                Move(FBuffer[FReadPos],tempbuf,Count);    //暂存数据到临时buf
                Move(tempbuf,Buffer,Count);
                Result := Count;
                FisWrap := True;
            end;
         end;
      end else begin               //无足够数据可读,可读取剩余数据
         if FWritePos > FReadPos then
         begin
            Move(FBuffer[FReadPos],Buffer,Datacount);           //暂存数据到临时buf
            Result := Datacount;
            FisWrap := false;
         end else begin
            //先判断尾部剩余空间有多少可读数据
            Li_RemainCount := FBufferLen - FReadPos;  //到缓冲区尾部还剩的缓冲量

            FillChar(tempbuf,MAX_BUFFER_LEN-1,0);
            Move(FBuffer[FReadPos],tempbuf,Li_RemainCount);           //暂存数据到临时buf
            Litemp := Datacount - Li_RemainCount;                     //剩余要读的数据

            Move(FBuffer[0],tempbuf[Li_RemainCount],Litemp);
            Move(tempbuf,Buffer,Datacount);
            Result := Datacount;
            FisWrap := false;
         end;
      end;
   finally
      Release();
   end;
end;

function TRingBuffer.ReSet():Boolean;
begin
   FReadPos := 0;
   FWritePos := 0;
   FIsCritical := False;
   FisWrap  := False;                //是否返回头部
   fillchar(FBuffer[0],FBufferLen-1,0);
   Result := True;
end;

procedure TRingBuffer.Require();
begin
  if FIsCritical then
  begin
    EnterCriticalSection(FCritical);
  end;
end;


{-------------------------------------------------------------------------------
  过程名:    Readbuffer
  作者:      黄健
  日期:      2007.08.25
  参数:      var Buffer  需要返回的数据;
          Count: Integer  需要读取的数据
  返回值:    Longint类型, 返回实际读取的数据量  <=0 表示读取错误或缓冲区空
-------------------------------------------------------------------------------}
function TRingBuffer.Readbuffer(var Buffer; Count: Integer): Longint;
var
  Li_RemainCount,Litemp : Integer;
  tempbuf:array[0..MAX_BUFFER_LEN] of Char;
  Datacount:Integer;
begin
   try
      Result := -1;
      Require();
      //if (Count < 0) or (Count > FBufferLen) then Exit;

      Datacount := GetDataCount();  //获取实际数据量
      if (Datacount <= 0) or ( Count = 0 ) then  //无数据可读的情况
      begin
         Result := 0;
         Exit;
      end;
      FillChar(tempbuf,MAX_BUFFER_LEN-1,0);
      if Datacount >= Count then   //有足够数据可读
      begin
         if FWritePos > FReadPos then
         begin
             Move(FBuffer[FReadPos],Buffer,Count);
             Inc(FReadPos,Count);
             Result := Count;
             FisWrap := false;
         end else begin
            //先判断尾部剩余空间有多少可读数据
            Li_RemainCount := FBufferLen - FReadPos;  //到缓冲区尾部还剩的缓冲量
            if Count > Li_RemainCount then            //尾部剩余数据都可读,把尾部数据全部读出
            begin
                Move(FBuffer[FReadPos],tempbuf,Li_RemainCount);          //暂存数据到临时buf
                Litemp := Count - Li_RemainCount;                        //剩余要读的数据
                Move(FBuffer[0],tempbuf[Li_RemainCount],Litemp);         //从头部读取剩余数据
                FReadPos := Litemp;
                Move(tempbuf,Buffer,Count);
                Result := Count;
                FisWrap := false;
            end else begin //当可用数据不到尾部,则直接读取
                Move(FBuffer[FReadPos],tempbuf,Count);    //暂存数据到临时buf
                Inc(FReadPos,Count);
                Move(tempbuf,Buffer,Count);
                Result := Count;
                FisWrap := True;
            end;
         end;
      end else begin               //无足够数据可读,可读取剩余数据
         if FWritePos > FReadPos then
         begin
            Move(FBuffer[FReadPos],Buffer,Datacount);           //暂存数据到临时buf
            Inc(FReadPos,Datacount);
            Result := Datacount;
            FisWrap := false;
         end else begin
            //先判断尾部剩余空间有多少可读数据
            Li_RemainCount := FBufferLen - FReadPos;  //到缓冲区尾部还剩的缓冲量

            FillChar(tempbuf,MAX_BUFFER_LEN-1,0);
            Move(FBuffer[FReadPos],tempbuf,Li_RemainCount);           //暂存数据到临时buf
            Litemp := Datacount - Li_RemainCount;                     //剩余要读的数据

            Move(FBuffer[0],tempbuf[Li_RemainCount],Litemp);
            FReadPos := Litemp;
            Move(tempbuf,Buffer,Datacount);
            Result := Datacount;
            FisWrap := false;
         end;
      end;
   finally
      Release();
   end;
end;


{-------------------------------------------------------------------------------
  过程名:    WriteBuffer
  作者:      黄健
  日期:      2007.08.25
  参数:      const Buffer;  要写入的数据
             Count: Integer  要写入数据大小
  返回值:    Longint         返回实际写入的数据量,<=0 表示写入错误或缓冲区满
-------------------------------------------------------------------------------}
function TRingBuffer.WriteBuffer(const Buffer; Count: Integer): Longint;
var
  Li_RemainCount,Litemp : Integer;
  tempbuf:array[0..MAX_BUFFER_LEN] of Char;
  EmptyCount:Integer;
begin
   try
      Require();
      EmptyCount := GetEmptyCount();    //获取可写入的数据大小
      if (Count < 0) or (Count > FBufferLen) then
      begin
         Result := -1;
         Exit;
      end;

      if (EmptyCount <=0)  then  //无数据可写
      begin
         Result := 0;
         Exit;
      end;

      //如果写入的缓冲区超过尾部,就跳回头部写入
      if EmptyCount >= Count  then
      begin
         if FWritePos < FReadPos then
         begin
            Move(Buffer,FBuffer[FWritePos],Count);
            Inc(FWritePos,Count);
            Result := Count;
            FisWrap := True;
         end else begin
            //判断是否到末尾,还有足够缓冲
            Li_RemainCount := FBufferLen - FWritePos;
            if (Li_RemainCount >= Count ) then
            begin
              Move(Buffer,FBuffer[FWritePos],Count);
              Inc(FWritePos,Count);
              Result := Count;
              FisWrap := False;
            end else begin  //末尾缓冲不够,返回头部读取
              Litemp :=Count - Li_RemainCount ;                 //尾部无法写入返回头部写入的数据
              FillChar(tempbuf,MAX_BUFFER_LEN-1,0);
              Move(Buffer,tempbuf,Count);                       //暂存数据到临时buf
              Move(tempbuf,FBuffer[FWritePos],Li_RemainCount);
              FWritePos := Litemp;
              Move(tempbuf[Li_RemainCount],FBuffer[0],Litemp);                  //返回头部写入剩余的数据
              Result := Count;
              FisWrap := false;
            end;
         end;
      end else begin //缓冲区不足以写入全部数据,可以写入部分数据
         if FWritePos < FReadPos then
         begin
            Result := FReadPos - FWritePos;
            Move(Buffer,FBuffer[FWritePos],Result);
            Inc(FWritePos,Result);
            FisWrap := True;
         end else begin
            //判断是否到末尾,还有足够缓冲
            Li_RemainCount := FBufferLen - FWritePos;
            FillChar(tempbuf,MAX_BUFFER_LEN-1,0);
            Move(Buffer,tempbuf,Count);                       //暂存数据到临时buf
            Move(tempbuf,FBuffer[FWritePos],Li_RemainCount);
            if FReadPos > 0 then
            begin
              FWritePos := FReadPos;
              Move(tempbuf[Li_RemainCount],FBuffer[0],FReadPos);                  //返回头部写入剩余的数据
              Result := Li_RemainCount + FReadPos;
              FisWrap := true;
            end else begin
              Inc(FWritePos,Li_RemainCount);
              Result := Li_RemainCount;
              FisWrap := False;
            end;
         end;
      end;
   finally
      Release();
   end;
end;


{-------------------------------------------------------------------------------
  过程名:    TRingBuffer.GetDataCount
  作者:      黄健
  日期:      2007.08.25
  参数:      无
  返回值:    Longint  返回当前可读的数据量
-------------------------------------------------------------------------------}
function TRingBuffer.GetDataCount: Longint;
begin
   try
     Require();
     if (FWritePos = 0) and (FReadPos = FWritePos) then
     begin
       Result := 0;
       Exit;
     end;
     if (FWritePos > FReadPos )  then
     begin
        Result := FWritePos - FReadPos;
     end else if FWritePos < FReadPos then begin
        Result := FBufferLen - FReadPos + FWritePos;
     end else if FWritePos = FReadPos then begin
        if FisWrap then Result := FBufferLen else Result := 0;
     end;
   finally
     Release();
   end;
end;


{-------------------------------------------------------------------------------
  过程名:    TRingBuffer.GetEmptyCount
  作者:      黄健
  日期:      2007.08.25
  参数:      无
  返回值:    Longint    返回当前可写的数据量
-------------------------------------------------------------------------------}
function TRingBuffer.GetEmptyCount: Longint;
begin
   try
     Require();
     if FWritePos > FReadPos  then
        Result := FBufferLen - FWritePos + FReadPos
     else if FWritePos < FReadPos then
       Result := FReadPos - FWritePos
     else if FWritePos = FReadPos then
     begin
        if FisWrap then Result := 0 else Result := FBufferLen;
     end;
   finally
     Release();
   end;
end;


{-------------------------------------------------------------------------------
  过程名:    TRingBuffer.ResizeBuffer
  作者:      黄健
  日期:      2007.08.25
  参数:      ABufferLen: Longint
  返回值:    Boolean     改变缓冲区大小,返回false表示失败,true表示成功
-------------------------------------------------------------------------------}
function TRingBuffer.ResizeBuffer(ABufferLen: Longint): Boolean;
begin
  Result := False;
  try
    Require();
    if ABufferLen < FBufferLen then Exit;
    SetLength(FBuffer,ABufferLen);
    FBufferLen := ABufferLen;
  finally
    Release();
  end;
end;

{ TRingBufferSyn }

constructor TRingBufferSyn.Create(ABufferLen: Integer);
begin
  inherited Create(ABufferLen);
  FIsCritical := True;
  FSynEvent := CreateEvent(nil,true, false,pchar('RingBuffer'));
end;

destructor TRingBufferSyn.Destory;
begin
  CloseHandle(FSynEvent);
  inherited Destory();
end;


{-------------------------------------------------------------------------------
  过程名:    TRingBufferSyn.ReadbufferSyn
  作者:      黄健
  日期:      2007.08.26
  参数:      var Buffer; Count, TimeOut: Integer
  返回值:    Integer ,返回-1表示错误,-2表示超时 ,>0表示实际读取的字节
-------------------------------------------------------------------------------}
function TRingBufferSyn.ReadbufferSyn(var Buffer; Count, TimeOut: Integer):Integer;
var
  ReadCount,RemainCount,ReadPos:Integer;
  Abuf:array[0..MAX_BUFFER_LEN-1] of Char;
  ATimeOut:Integer;
begin
  Result := -1;
  FillChar(Abuf,MAX_BUFFER_LEN-1,0);
  ReadCount := Readbuffer(Abuf,Count);
  if ReadCount < 0 then Exit;
  if ReadCount = Count then
  begin
     Move(Abuf,Buffer,Count);
     Result := Count;
  end else if ReadCount < Count then
  begin
     RemainCount := Count - ReadCount;
     ReadPos := ReadCount;
     ATimeOut := 0;
     while (RemainCount <= 0) do
     begin
       ReadCount := Readbuffer(Abuf[ReadPos],RemainCount);
       Inc(ReadPos,ReadCount);
       RemainCount := Count - ReadPos;
       //Sleep(1);
       Inc(ATimeOut);
       if ATimeOut >= TimeOut then
       begin
         Result := -2;
         Exit;
       end;
     end;
     Result := Count;    
  end;
end;


{-------------------------------------------------------------------------------
  过程名:    TRingBufferSyn.WriteBufferSyn
  作者:      黄健
  日期:      2007.08.26
  参数:      const Buffer; Count, TimeOut: Integer
  返回值:    Integer  Integer ,返回-1表示错误,-2表示超时 ,>0表示实际读取的字节
-------------------------------------------------------------------------------}
function TRingBufferSyn.WriteBufferSyn(const Buffer; Count, TimeOut: Integer):Integer;
var
  WriteCount,RemainCount,WritePos:Integer;
  Abuf:array[0..MAX_BUFFER_LEN-1] of Char;
  ATimeOut:Integer;
begin
  Result := -1;
  FillChar(Abuf,MAX_BUFFER_LEN-1,0);
  WriteCount := WriteBuffer(Buffer,Count);
  if WriteCount < 0 then Exit;
  if WriteCount = Count then
  begin
     Result := Count;
  end else if WriteCount < Count then
  begin
     Move(Buffer,Abuf,Count);
     RemainCount := Count - WriteCount;
     WritePos := WriteCount;
     ATimeOut := 0;
     while (RemainCount <= 0) do
     begin
       WriteCount := WriteBuffer(Abuf[WritePos],RemainCount);
       Inc(WritePos,WriteCount);
       RemainCount := Count - WritePos;
       //Sleep(1);
       Inc(ATimeOut);
       if ATimeOut >= TimeOut then
       begin
         Result := -2;
         Exit;
       end;      
     end;
     Result := Count;
  end;
end;

end.

评分

参与人数 1威望 +10 收起 理由
猫工 + 10 优秀文章

查看全部评分

回复

使用道具 举报

*滑块验证:

本版积分规则

QQ|手机版|小黑屋|Lazarus中国|Lazarus中文社区 ( 鄂ICP备16006501号-1 )

GMT+8, 2025-5-2 22:18 , Processed in 0.034127 second(s), 14 queries , Redis On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表