unit BufferedStream;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
aPart = ^TPart;
TPart =
record
PositionInStream:integer;
Length:integer;
FindAtPosition:integer;
end;
aBuffer=record
MemoryStream:TMemoryStream;
FileStream:TFilestream;
Swaped:Boolean;
PartsInfo:
array of aPart;
end;
TBufferedStream =
class(TStream)
Constructor Create(OpenFromFilename:
string;TempDirectory:
string);
function write(
Const Buffer;Count:integer):integer;
procedure ManageStreamType;
procedure ReadBuffer(
var Buffer;Count:integer);
procedure WriteBuffer(
Const Buffer;Count:integer);
function read(
var Buffer;Count:integer):integer;
private
{ Private-Deklarationen }
aFileStream:TFilestream;
Buffer:aBuffer;
swapsize:integer;
TempFileName:
string;
TempDirectory:
string;
procedure SetPos(newpos:int64);
procedure setsize(newsize:int64);
public
gSize:int64;
Pos:int64;
property Position: int64
read Pos
write SetPos;
property Size: int64
read gSize
write Setsize;
{ Public-Deklarationen }
end;
implementation
function GetTempFileName(TempDirectory:
String):
string;
var filename:
string;
i:integer;
begin
i:=0;
filename:=inttostr(i)+'
.part';
while fileexists(TempDirectory+filename)
do
begin
inc(i);
filename:=inttostr(i)+'
.part';
end;
result:=TempDirectory+filename;
end;
procedure TBufferedStream.setsize(newsize:int64);
begin
gSize:=newsize;
end;
procedure TBufferedStream.SetPos(newpos:int64);
begin
pos:=newpos;
end;
procedure TBufferedStream.ManageStreamType;
begin
with Buffer
do
begin
if Swaped
then
begin
if FileStream.size<(2/3)*Swapsize
then
begin
MemoryStream:=TMemoryStream.Create;
FileStream.Position:=0;
MemoryStream.CopyFrom(FileStream,FileStream.size);
freeandnil(Filestream);
DeleteFile(TempFileName);
Swaped:=False;
end
end
else
if MemoryStream.size>Swapsize
then
begin
Filestream.Create(TempFileName,fmCreate);
MemoryStream.Position:=0;
FileStream.CopyFrom(MemoryStream,MemoryStream.size);
freeandnil(MemoryStream);
Swaped:=True;
end;
end;
end;
procedure TBufferedStream.WriteBuffer(
Const Buffer;Count:integer);
var addsize:integer;
begin
with self.Buffer
do
begin
SetLength(PartsInfo,Length(PartsInfo)+1);
PartsInfo[Length(PartsInfo)-1]:=new(aPart);
PartsInfo[Length(PartsInfo)-1].PositionInStream:=Pos;
PartsInfo[Length(PartsInfo)-1].Length:=Count;
if Swaped
then
begin
FileStream.Position:=FileStream.size;
PartsInfo[Length(PartsInfo)-1].FindAtPosition:=Filestream.Position;
FileStream.
Write(Buffer,count);
end
else
begin
if MemoryStream.Size+Count>SwapSize
then
begin
Filestream:=TFilestream.Create(TempFileName,fmCreate);
MemoryStream.Position:=0;
FileStream.CopyFrom(MemoryStream,MemoryStream.size);
freeandnil(MemoryStream);
Swaped:=True;
FileStream.Position:=FileStream.size;
PartsInfo[Length(PartsInfo)-1].FindAtPosition:=Filestream.Position;
FileStream.
Write(Buffer,count);
end
else
begin
MemoryStream.Position:=MemoryStream.Size;
PartsInfo[Length(PartsInfo)-1].FindAtPosition:=MemoryStream.Position;
MemoryStream.
Write(Buffer,Count);
end;
end;
end;
addsize:=Count-(gSize-Pos);
Pos:=Pos+Count;
if addsize>0
then
gSize:=gSize+addsize;
end;
procedure TBufferedStream.ReadBuffer(
var Buffer;Count:integer);
var i:integer;
len:integer;
Start:integer;
Ende:integer;
PartStart:integer;
PartEnde:Integer;
InvolvedParts:TList;
MyPart:aPart;
BufferStream:Tmemorystream;
offset:integer;
ReadStart,ReadEnd:integer;
begin
//Zunächst alle Parts finden, die ganz oder Teilweise im zu lesenden Streamabschnitt stecken:
InvolvedParts:=TList.Create;
with self.Buffer
do
for i:=0
to length(PartsInfo)-1
do
begin
Start:=Pos;
Ende:=Pos+Count;
PartStart:=PartsInfo[i].PositionInStream;
PartEnde:=PartsInfo[i].PositionInStream+PartsInfo[i].length;
if ((PartStart>=Start)
and (PartStart<Ende))
or ((PartEnde>=Start)
and (PartEnde<Ende))
or ((PartStart<=Start)
and (PartEnde>=Ende))
then
InvolvedParts.Add(PartsInfo[i]);
end;
//Jetzt den Stream zusammensetzten
BufferStream:=TMemorystream.create;
BufferStream.SetSize(count);
if self.aFileStream<>
nil then
begin
aFileStream.Position:=Pos;
BufferStream.copyfrom(aFilestream,count);
end;
while InvolvedParts.count>0
do
begin
MyPart:=InvolvedParts.First;
PartStart:=MyPart.PositionInStream;
PartEnde:=PartStart+MyPart.Length;
If Start<PartStart
then ReadStart:=PartStart
else ReadStart:=Start;
If Ende<PartEnde
then ReadEnd:=Ende
else ReadEnd:=PartEnde;
len:= ReadEnd-ReadStart;
Bufferstream.position:= Start-ReadStart;
offset:=PartStart-ReadStart;
if offset>0
then offset:=0;
if self.Buffer.swaped
then
begin
self.Buffer.FileStream.position:=MyPart.FindAtPosition-offset;
BufferStream.CopyFrom(Self.Buffer.FileStream,len);
end
else
begin
self.Buffer.MemoryStream.position:=MyPart.FindAtPosition-offset;
BufferStream.CopyFrom(Self.Buffer.MemoryStream,len);
end;
InvolvedParts.Delete(InvolvedParts.IndexOf(MyPart));
end;
Pos:=Pos+count;
BufferStream.Position:=0;
BufferStream.
Read(Buffer,count);
freeandnil(BufferStream);
end;
function TBufferedStream.
read(
var Buffer;Count:integer):integer;
begin
readbuffer(Buffer,count);
end;
function TBufferedStream.
write(
Const Buffer;Count:integer):integer;
begin
WriteBuffer(Buffer,Count);
end;
Constructor TBufferedStream.Create(OpenFromFilename:
string;TempDirectory:
string);
var i: integer;
begin
Self.TempDirectory:=TempDirectory;
SwapSize:=5*1024;
Pos:=0;
gSize:=0;
TempFileName:=GetTempFileName(TempDirectory);
aFileStream:=Nil;
if OpenFromFileName<>'
'
then
begin
aFileStream:=TFilestream.Create(OpenFromFileName,fmOpenRead);
gSize:=aFilestream.Size;
aFilestream.Position:=0;
end;
with Buffer
do
begin
MemoryStream:=TMemoryStream.Create;
Swaped:=False;
SetLength(PartsInfo,0);
end;
end;
end.