Examples Delphi

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.