aboutsummaryrefslogblamecommitdiffstats
path: root/src/base/URingBuffer.pas
blob: ce51e209c58edc2011131e130b64e4605520431c (plain) (tree)































































































































                                                                                                                             
unit URingBuffer;

interface

{$IFDEF FPC}
  {$MODE Delphi}
{$ENDIF}

{$I switches.inc}

uses
  SysUtils;

type
  TRingBuffer = class
    private
      RingBuffer: PChar;
      BufferCount: integer;
      BufferSize: integer;
      WritePos: integer;
      ReadPos: integer;
    public
      constructor Create(Size: integer);
      destructor Destroy; override;
      function Read(Buffer: PChar; Count: integer): integer;
      function Write(Buffer: PChar; Count: integer): integer;
      procedure Flush();
  end;

implementation

uses
  Math;

constructor TRingBuffer.Create(Size: integer);
begin
  BufferSize := Size;

  GetMem(RingBuffer, Size);
  if (RingBuffer = nil) then
    raise Exception.Create('No memory');
end;

destructor TRingBuffer.Destroy;
begin
  FreeMem(RingBuffer);
end;

function TRingBuffer.Read(Buffer: PChar; Count: integer): integer;
var
  PartCount: integer;
begin
  // adjust output count
  if (Count > BufferCount) then
  begin
    //DebugWriteln('Read too much: ' + inttostr(count) +',count:'+ inttostr(BufferCount) + '/size:' + inttostr(BufferSize));
    Count := BufferCount;
  end;

  // check if there is something to do
  if (Count <= 0) then
  begin
    Result := Count;
    Exit;
  end;

  // copy data to output buffer

  // first step: copy from the area between the read-position and the end of the buffer
  PartCount := Min(Count, BufferSize - ReadPos);
  Move(RingBuffer[ReadPos], Buffer[0], PartCount);

  // second step: if we need more data, copy from the beginning of the buffer
  if (PartCount < Count) then
    Move(RingBuffer[0], Buffer[0], Count-PartCount);

  // mark the copied part of the buffer as free
  BufferCount := BufferCount - Count;
  ReadPos := (ReadPos + Count) mod BufferSize;

  Result := Count;
end;

function TRingBuffer.Write(Buffer: PChar; Count: integer): integer;
var
  PartCount: integer;
begin
  // check for a reasonable request
  if (Count <= 0) then
  begin
    Result := Count;
    Exit;
  end;

  // skip input data if the input buffer is bigger than the ring-buffer
  if (Count > BufferSize) then
  begin
    //DebugWriteln('Write skip data:' + inttostr(count) +',count:'+ inttostr(BufferCount) + '/size:' + inttostr(BufferSize));
    Buffer := @Buffer[Count - BufferSize];
    Count := BufferSize;
  end;

  // first step: copy to the area between the write-position and the end of the buffer
  PartCount := Min(Count, BufferSize - WritePos);
  Move(Buffer[0], RingBuffer[WritePos], PartCount);

  // second step: copy data to front of buffer
  if (PartCount < Count) then
    Move(Buffer[PartCount], RingBuffer[0], Count-PartCount);

  // update info
  BufferCount := Min(BufferCount + Count, BufferSize);
  WritePos := (WritePos + Count) mod BufferSize;
  // if the buffer is full, we have to reposition the read-position
  if (BufferCount = BufferSize) then
    ReadPos := WritePos;

  Result := Count;
end;

procedure TRingBuffer.Flush();
begin
  ReadPos := 0;
  WritePos := 0;
  BufferCount := 0;
end;

end.