{*******************************************************}
{ }
{ 环形无锁缓冲Delphi版 }
{ 2016.04.13 By Lance }
{ 联系方式QQ:286922468 }
{ }
{*******************************************************}
{
本源码受原作者节制,你可以任意复制、修改、使用,如发现
BUG请报告给我们。为了倡导开源,你的一切修改版本,我们
希望也能开源。
警告:使用本项目源码产生的一切后果由使用者自行负责。
注:TJsRingBuffer 基于寰子博客中C++源码,版权归原作者所有
寰子博客原文 http://blog.youkuaiyun.com/xocoder/article/details/7880769
2016.04.13
基于寰子博客中C++源码,实现环形无锁缓冲
2017.10.23
增加动态扩容功能
兼容Delphi XE 10.1
}
unit JsRingBuffer;
{.$DEFINE DeBugLog}
//仅适用于一个线程写,一个线程读的情况(不适用于多个线程写或多个线程读)
interface
uses Windows;
const RingBuffer_ReadPos_And_WritePos_Size=2;
type
TJsRingBuffer=class
private
BufferData:Pointer;
BufferSize,DefaultSize,FWritePosition,FReadPosition:DWord;
function CopyDataWithAddReadPosOption(Dest:Pointer;DestSize,CopySize:DWord;AddReadPos:Bool):Bool;
function PushData2(Data:Pointer;Size:DWord):Bool;
procedure AddRingSize;//自动扩展缓冲区大小
protected
public
function GetUsedSize:DWord;//数据的大小
function GetRingSize:DWord;//缓冲区总大小
function GetFreeSize:DWord;//空闲大小
function PopData(Dest:Pointer;DestSize,PopSize:DWord):Bool; overload;//读出数据
function PopData(PopSize:DWord):Bool; overload;//丢弃数据
function PushData(Data:Pointer;Size:DWord):Bool;//插入数据
function CopyData(Dest:Pointer;DestSize,CopySize:DWord):Bool;//只读方式复制数据(不会移动读指针)
function ClearData:Bool;//清空所有数据
procedure RestoreDefaultSize;//将缓冲区还原到默认大小,释放一部份内存
public
constructor Create(const Size:DWord);
destructor Destroy; override;
end;
implementation
constructor TJsRingBuffer.Create(const Size:DWord);
begin
inherited Create;
BufferSize:=(Size+RingBuffer_ReadPos_And_WritePos_Size);
FWritePosition:=1;
FReadPosition:=0;
BufferData:=nil;
GetMem(BufferData,BufferSize);
ZeroMemory(BufferData,BufferSize);
DefaultSize:=BufferSize;
end;
destructor TJsRingBuffer.Destroy;
begin
FreeMem(BufferData);
inherited Destroy;
end;
function TJsRingBuffer.GetRingSize:DWord;
begin
Result:=BufferSize;
end;
function TJsRingBuffer.GetUsedSize:DWord;
var ReadPos,WritePos:DWord;
begin
ReadPos:=FReadPosition;
WritePos:=FWritePosition;
if WritePos>ReadPos then begin
Result:=WritePos-ReadPos-1;
Exit;
end else if (WritePos<ReadPos) then begin
Result:=(BufferSize-ReadPos-1)+FWritePosition;
Exit;
end else begin
{$IFDEF DeBugLog}
OutputDebugString(PChar('error:write pos equal read pos'));
{$ENDIF}
Result:=0;
end;
end;
function TJsRingBuffer.GetFreeSize:DWord;
begin
Result:=BufferSize-(GetUsedSize+RingBuffer_ReadPos_And_WritePos_Size);
end;
function TJsRingBuffer.ClearData:Bool;
begin
Result:=PopData(GetUsedSize);
end;
procedure TJsRingBuffer.RestoreDefaultSize;
begin
if BufferSize=DefaultSize then Exit;
BufferSize:=DefaultSize;
ReallocMem(BufferData,BufferSize);
FWritePosition:=1;
FReadPosition:=0;
end;
procedure TJsRingBuffer.AddRingSize;
var
OldData:Pointer;
i:Integer;
begin
i:=GetUsedSize;
GetMem(OldData,i);
CopyDataWithAddReadPosOption(OldData,i,i,True);//取出缓冲区的数据
//增加一倍容量
BufferSize:=BufferSize*2;
ReallocMem(BufferData,BufferSize);//重新分配内存
//重置开始位置
FWritePosition:=1;
FReadPosition:=0;
PushData2(OldData,i);//把旧数据重新插入
FreeMem(OldData);
{$IFDEF DeBugLog}
OutputDebugString(PChar('发生一次写入溢出,已自动扩大缓冲区'));
{$ENDIF}
end;
function TJsRingBuffer.PushData(Data:Pointer;Size:DWord):Bool;
begin
while GetFreeSize<Size do AddRingSize;//空闲数不足,无法写入,溢出,增加自动一倍缓冲区大小(写速比读速快时,会出现这种情况)
Result:=PushData2(Data,Size);
end;
function TJsRingBuffer.PushData2(Data:Pointer;Size:DWord):Bool;
var ReadPos,WritePos,lenFromWritePosToBufferEnd,SecondPartLen:DWord;
begin
// if (GetFreeSize<Size) then begin
// //写入溢出,这种情况不可能出现
// Result:=False;
// Exit;
// end;
ReadPos:=FReadPosition;
WritePos:=FWritePosition;
if (WritePos>ReadPos) then begin//写入在未读数据后面,初始化时,写入点为1,读取点为0
lenFromWritePosToBufferEnd:=BufferSize-WritePos;
if (Size<=lenFromWritePosToBufferEnd) then begin//写入的数据,未跨环
CopyMemory(Pointer(PAnsiChar(BufferData)+FWritePosition),Pointer(Data),Size);
Inc(FWritePosition,Size);
//若写完数据后,新写入点刚好是在环最尾,就把写入点定位到环头
if (FWritePosition=BufferSize) then FWritePosition:=0
// else if (FWritePosition>BufferSize) then begin
// //若写完数据后,新写入点比环还大,出错处理
// //这里不可能发生 Size<=lenFromWritePosToBufferEnd时,不可能出现这种情况
// {$IFDEF DeBugLog}
// OutputDebugString(PChar('wirtepos cannot bigger than Size'));
// {$ENDIF}
// Result:=False;
// Exit;
// end
;
Result:=True;
Exit;
end else begin
// 先拷贝前一部分到缓冲区尾部
CopyMemory(Pointer(PAnsiChar(BufferData)+FWritePosition),Pointer(Data),lenFromWritePosToBufferEnd);
SecondPartLen:=Size-lenFromWritePosToBufferEnd;
// 拷贝后一部分到缓冲区前部
CopyMemory(Pointer(PAnsiChar(BufferData)),Pointer(PAnsiChar(Data)+lenFromWritePosToBufferEnd),SecondPartLen);
FWritePosition:=SecondPartLen;
Result:=True;
Exit;
end;
end else if (WritePos<ReadPos) then begin
CopyMemory(Pointer(PAnsiChar(BufferData)+WritePos),Pointer(Data),Size);
Inc(FWritePosition,Size);
Result:=True;
Exit;
end else begin
{$IFDEF DeBugLog}
OutputDebugString(PChar('error:write pos equal read pos'));
{$ENDIF}
Result:=False;
Exit;
end;
end;
function TJsRingBuffer.CopyData(Dest:Pointer;DestSize,CopySize:DWord):Bool;
begin
Result:=CopyDataWithAddReadPosOption(Dest,DestSize,CopySize,False);
end;
function TJsRingBuffer.PopData(Dest:Pointer;DestSize,PopSize:DWord):Bool;
begin
Result:=CopyDataWithAddReadPosOption(Dest,DestSize,popSize,True);
end;
function TJsRingBuffer.PopData(PopSize:DWord):Bool;
begin
Result:=CopyDataWithAddReadPosOption(nil,0,PopSize,True);
end;
function TJsRingBuffer.CopyDataWithAddReadPosOption(Dest:Pointer;DestSize,CopySize:DWord;AddReadPos:Bool):Bool;
var UsedSize,WritePos,ReadPos,LenFromReadPosToBufferEnd,SecondPartLen:DWord;
begin
UsedSize:=GetUsedSize();
if (UsedSize<CopySize) then begin
{$IFDEF DeBugLog}
OutputDebugString(PChar('Data is not enought to copy'));
{$ENDIF}
Result:=False;
Exit;
end;
if (Dest<>nil) then begin
if (DestSize<CopySize) then begin
{$IFDEF DeBugLog}
OutputDebugString(PChar('Dest buffer Size is smaller than copy Size'));
{$ENDIF}
Result:=False;
Exit;
end;
end;
WritePos:=FWritePosition;
ReadPos:=FReadPosition;
if (WritePos>ReadPos) then begin//要读取得数据,未跨环
if (Dest<>nil) then begin
CopyMemory(Pointer(Dest),Pointer(PAnsiChar(BufferData)+ReadPos+1),CopySize);
{$IFDEF DeBugLog}
//读出数据后,清理无用的数据
if addReadPos then ZeroMemory(Pointer(PAnsiChar(BufferData)+ReadPos+1),CopySize);
{$ENDIF}
end;
if addReadPos then Inc(FReadPosition,CopySize);
Result:=True;
Exit;
end else if (WritePos<ReadPos) then begin//要读取的数据,跨环
LenFromReadPosToBufferEnd:=BufferSize-ReadPos-1;
if (CopySize<=LenFromReadPosToBufferEnd) then begin
if (Dest<>nil) then begin
CopyMemory(Pointer(Dest),Pointer(PAnsiChar(BufferData)+ReadPos+1),CopySize);
{$IFDEF DeBugLog}
//读出数据后,清理无用的数据
if addReadPos then ZeroMemory(Pointer(PAnsiChar(BufferData)+ReadPos+1),CopySize);
{$ENDIF}
end;
if addReadPos then begin
inc(FReadPosition,CopySize);
if not(FReadPosition<BufferSize) then begin
{$IFDEF DeBugLog}
OutputDebugString(PChar('FReadPosition => BufferSize'));
{$ENDIF}
Result:=False;
Exit;
end;
end;
Result:=True;
Exit;
end else begin
SecondPartLen:=CopySize-LenFromReadPosToBufferEnd;
if Dest<>nil then begin
CopyMemory(Pointer(Dest),Pointer(PAnsiChar(BufferData)+ReadPos+1),LenFromReadPosToBufferEnd);
CopyMemory(Pointer(PAnsiChar(Dest)+LenFromReadPosToBufferEnd),Pointer(BufferData),SecondPartLen);
end;
if addReadPos then FReadPosition:=SecondPartLen-1;
Result:=True;
Exit;
end;
end else begin
{$IFDEF DeBugLog}
OutputDebugString(PChar('error:write pos equal read pos'));
{$ENDIF}
Result:=False;
Exit;
end;
end;
end.
Delphi版 环形无锁缓冲(二)
最新推荐文章于 2024-02-27 23:43:41 发布
