program CircularBufferIntU;
{$MODE DELPHI}
uses
SysUtils;
type
TIntegerCircularBuffer = class(TObject)
private
FBuffer: PIntegerArray;
FFullSize: integer;
FWritePos: integer;
FReadPos: integer;
FSampleCount: integer;
function GetIntAtIndex(AIndex: word): integer;
function GetReadAvail: integer;
function GetWriteAvail: integer;
public
constructor Create(ASize: Integer); virtual;
destructor Destroy; override;
function AddSample(const AValue: integer): boolean;
procedure Delete(ACount: integer);
procedure SetFillPoint(AFillPoint: integer);
property FullSize: integer read FFullSize;
property ReadAvail: integer read GetReadAvail;
property WriteAvail: integer read GetWriteAvail;
property SampleCount: integer read FSampleCount;
property Samples[Index: word]: integer read GetIntAtIndex; default;
end;
constructor TIntegerCircularBuffer.Create(ASize: Integer);
begin
inherited Create;
FFullSize := ASize;
FSampleCount := 0;
FReadPos := 0;
FWritePos := 0;
FBuffer := AllocMem(ASize * sizeof(integer));
end;
destructor TIntegerCircularBuffer.Destroy;
begin
FreeMem(FBuffer);
inherited Destroy;
end;
function TIntegerCircularBuffer.AddSample(const AValue: integer): boolean;
begin
result := true;
if (GetWriteAvail < 1) then begin
result := false;
Exit;
end;
FBuffer^[FWritePos] := AValue;
if (FWritePos = FFullSize -1) then FWritePos := 0 else inc(FWritePos);
inc(FSampleCount);
end;
procedure TIntegerCircularBuffer.Delete(ACount: integer);
begin
if (ACount <= 0 ) then begin
Exit;
end;
if (ACount >= FSampleCount) then begin
FSampleCount := 0;
FReadPos := FWritePos;
Exit;
end;
inc(FReadPos, ACount);
FReadPos := FReadPos mod FFullSize;
dec(FSampleCount, ACount);
end;
function TIntegerCircularBuffer.GetIntAtIndex(AIndex: word): integer;
var
ii: integer;
begin
ii := FReadPos + AIndex;
if (ii > FFullSize -1) then
ii := ii - FFullSize;
result := FBuffer^[ii];
end;
function TIntegerCircularBuffer.GetReadAvail: integer;
begin
result := FSampleCount;
end;
function TIntegerCircularBuffer.GetWriteAvail: integer;
begin
result := FFullSize - FSampleCount;
end;
procedure TIntegerCircularBuffer.SetFillPoint(AFillPoint: integer);
begin
FReadPos := 0;
FWritePos := AFillPoint;
FillChar(FBuffer^[0], SizeOf(Integer)* FFullSize, 0);
end;
procedure ReadValues(icb: TIntegerCircularBuffer; NextExpectedRead: Integer; const msg2: string);
var
j: Integer;
ReadVal: Integer;
msg3: string;
begin
Assert(icb.WriteAvail = 0, msg2);
for j := 0 to Pred(icb.FullSize) do begin
msg3 := Format('%s; j = %d', [msg2, j]);
ReadVal := icb[j];
Assert(ReadVal = NextExpectedRead, msg3);
Inc(NextExpectedRead);
end;
end;
procedure TestUsingBuffer(Size: Integer);
var
icb: TIntegerCircularBuffer;
i: Integer;
NextAdd: Integer;
NextExpectedRead: Integer;
msg1, msg2: string;
begin
icb := TIntegerCircularBuffer.Create(Size);
try
msg1 := Format('size = %d', [Size]);
Assert(icb.FullSize = Size, msg1);
Assert(icb.ReadAvail = 0, msg1);
Assert(icb.WriteAvail = Size, msg1);
Assert(icb.SampleCount = 0, msg1);
NextAdd := 1000;
NextExpectedRead := 1000;
for i := 1 to 1000 do begin
msg2 := Format('size = %d; i = %d', [Size, i]);
while icb.WriteAvail > 0 do begin
icb.AddSample(NextAdd);
Inc(NextAdd);
end;
ReadValues(icb, NextExpectedRead, msg2);
icb.Delete(5);
Inc(NextExpectedRead, 5);
Assert(icb.ReadAvail = Size - 5, msg2);
Assert(icb.WriteAvail = 5, msg2);
Assert(icb.SampleCount = Size - 5, msg2);
end;
finally
icb.Free;
end;
end;
begin
TestUsingBuffer(17);
TestUsingBuffer(29);
TestUsingBuffer(501);
Writeln('Done');
end.