Mega Code Archive

 
Categories / Delphi / Examples
 

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.