unit FiFoBuffer;
{*******************************************************************************
Type : FifoBuffer
Filename : FifoBuffer.pas
Date : 2008-03-19
Version : 1.0.0.0
Last modified : 2008-03-19
Author : David Huettig a.k.a littleDave
URL : [url]www.godlikesoft.de[/url]
Copyright : Copyright (c) 2008 David Huettig
History :
v. 1.0.0.0
==========
- Initial release
*******************************************************************************}
{*******************************************************************************
Copyright (c) 2008, David Huettig ["copyright holder(s)"]
1. You are free to use this unit in any project, freeware or commercial.
You are allowed to release this unit in OpenSource-projects - as long
as you do not claim that you are the original author.
2. You are NOT allowed to remove these comment lines
3. If you redistribute this unit, please fill the history with any
changes you made.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************}
{*******************************************************************************
Diese Unit erstellt einen FiFo-Puffer (FirstIn - FirstOut - Puffer). Dieser
Puffer erlaubt es, sequentiell Daten in den Puffer zu schreiben um diese dann
später IN DER GLEICHEN REIHENFOLGE auslesen zu können. Die größe des Puffers
ist fix und wird am Anfang festgelegt. Es können nur soviel Bytes im Puffer
gespeichert werden, wie die maximale Größe.
Dieser Puffer ist bis auf die Funktion "AllocateMemory", die durch die Property
TotalSize := xxx
aufgerufen wird, thread-save. Das heißt, es kann ohne Probleme mit Threads
gearbeitet werden, ohne CriticalSections oder Synchronizes zu benutzen. Dies
wird alles intern geregelt!
===============================================
= Kurzes Beispiel zur Benutzung =
===============================================
var Buffer: TFiFoBuffer; // Das Puffer-Object
len : integer; // Daten die gelesen / geschrieben werden sollen
s : string; // Daten die gelesen / geschrieben werden sollen
begin
Buffer := TFiFoBuffer.Create(1024*5); // Puffer mit einer Füllmenge von 5KB erstellen
try
s := Edit1.Text; // Den ersten Text auslesen
len := length(s); // die Länge des Textes auslesen
if Buffer.Write(len, SizeOf(len)) = -1 then // Die Länge des Textes speichern
raise Exception.Create('Fehler beim Schreiben - Puffer zu klein');
if Buffer.Write(s[1], len) = -1 then // Den Text an sich speichern
raise Exception.Create('Fehler beim Schreiben - Puffer zu klein');
s := Edit2.Text; // Dann den zweiten Text auslesen
len := length(s); // die Länge des Textes auslesen
if Buffer.Write(len, SizeOf(len)) = -1 then // Die Länge des Textes speichern
raise Exception.Create('Fehler beim Schreiben - Puffer zu klein');
if Buffer.Write(s[1], len) = -1 then // Den Text an sich speichern
raise Exception.Create('Fehler beim Schreiben - Puffer zu klein');
(* Jetzt stehen beide Strings im Puffer - jetzt kann ich sie auslesen *)
if Buffer.Read(len, SizeOf(len)) = -1 then // Zuerst die Länge auslesen
raise Exception.Create('Fehler beim Lesen - nicht genügend Daten im Puffer');
SetLength(s, len); // Den String initialisieren
if Buffer.Read(s[1], len) = -1 then // Den String auslesen
raise Exception.Create('Fehler beim Lesen - nicht genügend Daten im Puffer');
(* Jetzt steht in s wieder der Inhalt von Edit1 *)
Memo1.Lines.Add(s); // String ausgeben
if Buffer.Read(len, SizeOf(len)) = -1 then // Zuerst die Länge auslesen
raise Exception.Create('Fehler beim Lesen - nicht genügend Daten im Puffer');
SetLength(s, len); // Den String initialisieren
if Buffer.Read(s[1], len) = -1 then // Den String auslesen
raise Exception.Create('Fehler beim Lesen - nicht genügend Daten im Puffer');
(* Jetzt steht in s wieder der Inhalt von Edit2 *)
Memo1.Lines.Add(s); // String ausgeben
finally
Buffer.Free;
end;
end;
*******************************************************************************}
interface
uses
SyncObjs;
type
TFiFoBuffer =
class(TObject)
private
FData : Pointer;
FSize : integer;
FReadPointer : integer;
FWritePointer : integer;
FWriteSection : TCriticalSection;
FReadSection : TCriticalSection;
protected
function GetFilledSize : integer;
procedure AllocateMemory(newSize: integer);
public
constructor Create(Size: integer);
destructor Destroy;
override;
procedure ResetBuffer;
function Write(
const Buffer; Count: integer): integer;
function Read(
var Buffer; Count: integer): integer;
property TotalSize : integer
read FSize
write AllocateMemory;
property FilledSize : integer
read GetFilledSize;
end;
implementation
{ TFiFoBuffer }
procedure TFiFoBuffer.AllocateMemory(newSize: integer);
begin
if newSize < 10
then // 10 Bytes sind minimum - könnten theoretisch auch 2 sein, doch wer braucht
newSize := 10;
// schon einen Buffer mit 2 Byte
if Assigned(FData)
then // Ist bereits ein Puffer vorhanden
FreeMem(FData, FSize);
// Müssen wir ihn erst freigeben
GetMem(FData, newSize);
// Speicher reservieren
FSize := newSize;
// Größe speichern
ResetBuffer;
// Lese- und Schreib "Kopf" zurücksetzen
end;
constructor TFiFoBuffer.Create(Size: integer);
begin
inherited Create;
FReadSection := TCriticalSection.Create;
FWriteSection := TCriticalSection.Create;
AllocateMemory(Size);
end;
destructor TFiFoBuffer.Destroy;
begin
if Assigned(FData)
then
FreeMem(FData, FSize);
FWriteSection.Free;
FReadSection.Free;
inherited;
end;
function TFiFoBuffer.GetFilledSize: integer;
begin
FWriteSection.Enter;
// Benötige Zugriff auf FWritePointer
FReadSection.Enter;
// Benötige Zugriff auf FReadPointer
try
if (FReadPointer > FWritePointer)
then
result := FSize - FReadPointer + FWritePointer
// Puffer hat einen Rap-Around
else
result := FWritePointer - FReadPointer;
finally
FReadSection.Leave;
// Und die kritischen Bereiche wieder verlassen
FWriteSection.Leave;
end;
end;
function TFiFoBuffer.
Read(
var Buffer; Count: integer): integer;
var Address : Pointer;
SplitSize : integer;
begin
result := -1;
// Ergebniss initialisieren
if GetFilledSize < Count
then
exit;
// Es soll mehr gelesen werden, als im Puffer drinnsteht - abbrechen
FReadSection.Enter;
// Benötige Zugriff auf FReadPointer
try
if FSize - FReadPointer < Count
then // Daten sind gesplittet
begin
SplitSize := FSize - FReadPointer;
Move(Pointer(LongInt(FData) + FReadPointer)^, Buffer , SplitSize);
// Den hinteren Teil lesen
FReadPointer := 0;
Address := Addr(Buffer);
Address := Pointer(LongInt(Address) + (SplitSize));
Move(FData^, Address^, Count - SplitSize);
// Den vorderen Teil lesen
inc(FReadPointer, Count - SplitSize);
// Read Pointer anpassen
result := Count;
// Anzahl gelesener Bytes zurückgeben
end else
begin // Daten sind nicht gesplittet
Move(Pointer(LongInt(FData) + FReadPointer)^, Buffer, Count);
// Einfach fröhlich auslesen
result := Count;
// Anzahl der gelesenen Bytes zurückgeben
inc(FReadPointer, Count);
// Read-Position für den nächsten Zugriff anpassen
end;
if FReadPointer > FSize
then // Falls doch ein Splitting erfolgt ist - anpassen
FReadPointer := FReadPointer - FSize;
finally
FReadSection.Leave;
// LesePointer wieder Freigeben
end;
end;
procedure TFiFoBuffer.ResetBuffer;
begin
FReadSection.Enter;
// Benötige Zugriff auf Lese-Pointer
FWriteSection.Enter;
// Benötige Zugriff auf Schreib-Pointer
try
FReadPointer := 0;
// Positionen zurücksetzen
FWritePointer := 0;
finally
FReadSection.Leave;
// Und Zugriff wieder Freigeben
FWriteSection.Leave;
end;
end;
function TFiFoBuffer.
Write(
const Buffer; Count: integer): integer;
var Address : Pointer;
SplitSize : integer;
begin
result := -1;
// Anzahl geschriebener Bytes initialisieren
if Count < 1
then // Wer will schon 0 oder -5 byte schreiben
exit;
// Falls es jemanden gibt, bekommt er nichts
if FSize - GetFilledSize < Count
then // Falls nicht mehr genügend Platz ist
exit;
// Kann nicht mehr in den Puffer geschrieben werden - abbrechen
FWriteSection.Enter;
// Benötige Zugriff auf SchreibPointer
try
if FSize - FWritePointer < Count
then // Daten müssen gesplittet werden
begin
SplitSize := FSize - FWritePointer;
Address := Addr(Buffer);
Move(Buffer, Pointer(LongInt(FData) + FWritePointer)^, SplitSize);
// Ersten Block ans Ende schreiben
FWritePointer := 0;
Address := Pointer(LongInt(Address) + (SplitSize));
Move(Address^, FData^, Count - SplitSize);
// Zweiten Block an den Anfang schreiben
inc(FWritePointer, Count - SplitSize);
// Write-Position setzen
result := Count;
// Anzahl geschrieber Bytes zurückgeben
end else
begin // Daten müssen nicht gesplittet werden
Move(Buffer, Pointer(LongInt(FData) + FWritePointer)^, Count);
// Fröhliches Kopieren
result := Count;
// Anzahl geschriebener Bytes zurückgeben
inc(FWritePointer, Count);
// Schrei-Position anpassen
end;
if FWritePointer > FSize
then // Falls doch ein Splitting erfolgt ist - anpassen
FWritePointer := FWritePointer - FSize;
finally
FWriteSection.Leave;
// Und nun den Zugriff auf FWritePointer wieder freigeben
end;
end;
end.