FIFO stream

A Delphi class implementation of a FIFO stream.

A FIFO stream (First In First Out) is useful when you need to constantly receive data to a member buffer, but don't want that memory buffer to constantly grow.



It is used typically when receiving an Mp3 audio stream from the Internet. You might need to hold on until a full Mp3 buffer has been received, but don't want your buffer to continue to grow to the full size of the Mp3 being streamed.



The trick is to implement a circular buffer, once you write past the end of the buffer your position is reset to the start of the buffer. The only problem really that may occur is that you may receive data quicker than you process it, resulting in a buffer overflow. It is advisable to make the buffer too large in the first place. Here is my own implementation. Please do not remove the copyright notice.



<==========SNIP=========>

//(C) Peter Morris - pete@stuckindoors.com / pete@droopyeyes.com

unit FIFOStream;



interface

uses

  Windows, Messages, SysUtils, Classes;



type

  EFIFOStream = class(Exception);



  TFIFOStream = class(TObject)

  private

    FData : PChar;

    FMemorySize : Integer;

    FBufferEnd,

    FBufferStart : Integer;

  protected

  public

    constructor Create(aSize : Integer); virtual;

    destructor Destroy; override;



    function BufferReadSize : Integer;

    function BufferWriteSize : Integer;



    procedure Clear;

    procedure Peek(const aBuffer : Pointer; Count : Integer);

    procedure Read(const aBuffer : Pointer; Count : Integer);

    procedure Write(const aSource : Pointer; Count : Integer);



  published

  end;



implementation



procedure CharMove(const Source; var Dest; Count : Integer);

asm

//Note: When this function is called, delphi passes the parameters as follows

//ECX = Count

//EAX = Const Source

//EDX = Var Dest



        //If no bytes to copy, just quit altogether, no point pushing registers

        cmp ECX,0

        Je @JustQuit



        //Preserve the critical delphi registers

        push ESI

        push EDI



        //move Source into ESI (generally the SOURCE register)

        //move Dest into EDI (generally the DEST register for string commands)

        //This may not actually be neccessary, as I am not using MOVsb etc

        //I may be able just to use EAX and EDX, there may be a penalty for

        //not using ESI, EDI but I doubt it, this is another thing worth trying !

        mov ESI, EAX

        mov EDI, EDX



        //The following loop is the same as repNZ MovSB, but oddly quicker !

    @Loop:

        //Get the source byte

        Mov AL, [ESI]

        //Point to next byte

        Inc ESI

        //Put it into the Dest

        mov [EDI], AL


        //Point dest to next position

        Inc EDI

        //Dec ECX to note how many we have left to copy

        Dec ECX

        //If ECX <> 0 then loop

        Jnz @Loop



        pop EDI

        pop ESI

    @JustQuit:

end;





{ TFIFOStream }



function TFIFOStream.BufferReadSize: Integer;

begin

  if FBufferEnd >= FBufferStart then //Not looped

    Result := FBufferEnd - FBufferStart

  else //Looped

    Result := FMemorySize - FBufferStart + FBufferEnd;

end;



function TFIFOStream.BufferWriteSize: Integer;

begin

  Result := FMemorySize - BufferReadSize;

end;



procedure TFIFOStream.Clear;

begin

  FBufferEnd := 0;

  FBufferStart := 0;

end;



constructor TFIFOStream.Create(aSize: Integer);

begin

  inherited Create;

// if aSize < 1024 then

// raise EFIFOStream.Create('Buffer size must be at least 1K.');

  FMemorySize := aSize;

  Getmem(FData, FMemorySize);

  FBufferStart := 0;

  FBufferEnd := 0;

end;



destructor TFIFOStream.Destroy;

begin

  FreeMem(FData);

  inherited;

end;



procedure TFIFOStream.Peek(const aBuffer: Pointer; Count: Integer);

var

  OrigStart : Integer;

begin

  OrigStart := FBufferStart;

  try

    Read(aBuffer, Count);

  finally

    FBufferStart := OrigStart;

  end;

end;



procedure TFIFOStream.Read(const aBuffer : Pointer; Count: Integer);

var

  Source,

  Dest : PChar;

  CopyLen : Integer;

begin

  Source := @FData[FBufferStart];

  Dest := aBuffer;

  if BufferReadSize < Count then

    raise EFIFOStream.Create('Buffer under-run.');



  CopyLen := FMemorySize - FBufferStart;

  if CopyLen > Count then CopyLen := Count;

  CharMove(Source^,Dest^,CopyLen);



  Inc(FBufferStart,CopyLen);



  //If looped

  if FBufferStart >= FMemorySize then begin

    FBufferStart := FBufferStart - FMemorySize;

    Read(@Dest[CopyLen],Count-CopyLen);

  end;

end;



procedure TFIFOStream.Write(const aSource : Pointer; Count: Integer);

var

  Source,

  Dest : PChar;

  CopyLen : Integer;

begin

  Source := aSource;

  Dest := @FData[FBufferEnd];

  if BufferWriteSize < Count then

    raise EFIFOStream.Create('Buffer over-run.');



  CopyLen := FMemorySize - FBufferEnd;

  if CopyLen > Count then CopyLen := Count;

  CharMove(Source^,Dest^,CopyLen);

  Inc(FBufferEnd,CopyLen);



  //If looped

  if FBufferEnd >= FMemorySize then begin

    FBufferEnd := FBufferEnd - FMemorySize;

    Write(@Source[CopyLen],Count-CopyLen);

  end;

end;



end.

 

Share this article!

Follow us!

Find more helpful articles: