//(C) Peter Morris - pete@stuckindoors.com / pete@droopyeyes.com
unit uFIFOStream;
interface
uses
Windows, Messages, SysUtils, Classes;
type
EFIFOStream =
class(
Exception);
TFIFOStream =
class(TObject)
private
FData : PByte;
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 ByteMove(
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 : PByte;
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;
ByteMove(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 : PByte;
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;
ByteMove(Source^,Dest^,CopyLen);
Inc(FBufferEnd,CopyLen);
//If looped
if FBufferEnd >= FMemorySize
then
begin
FBufferEnd := FBufferEnd - FMemorySize;
Write(@Source[CopyLen],Count-CopyLen);
end;
end;
end.