Mega Code Archive
Combine streams in one Delphi TStream object
Title: Combine streams in one Delphi TStream object
Question: This article represents a simple TStream class whose data is based on the number of another Delphi streams.
Answer:
Abstract
This article represents a simple TStream class whose data is based on the number of another Delphi streams.
There are some tasks when you may want to provide the data combined from different parts via TStream interface. The most obvious way is to create the TMemoryStream class instance and copy all data parts (streams in our case) into this single object. This solution has only one good thing - the simplicity and you can use it for sure for combining of small footprint streams without bothering about the reverse of the medal. While working with the large size streams or having big number of streams such primitive copying of data chunks may lead to bad application performance and irrational using of virtual memory.
TMultiStream Class Declaration
In order to effectively combine number of streams into the single TStream class descendant you will need to organize access to streams data in appropriate order.
We will create new TMultiStream inherited from Delphi base abstract TStream class. Methods Read, Seek and Write need to be overriden and implemented within TMultiStream class.
The code below shows the implementation of the Read method of TMultiStream class:
function TMultiStream.Read(var Buffer; Count: Integer): Longint;
var
i: Integer;
buf_pos: PChar;
len, bytesRead: Longint;
begin
len := 0;
Result := 0;
buf_pos := PChar(@Buffer);
for i := 0 to FList.Count - 1 do
begin
if (FPosition begin
GetStream(i).Position := FPosition - len;
bytesRead := GetStream(i).Read(buf_pos^, Count);
Inc(Result, bytesRead);
buf_pos := buf_pos + bytesRead;
Inc(FPosition, bytesRead);
if (bytesRead begin
Dec(Count, bytesRead);
end else
begin
break;
end;
end;
Inc(len, GetStream(i).Size);
end;
end;
This method walks through all streams being stored within the FList field of TMultiStream class and checks each of them for matching the TMultiStream Position. If the current stream position plus total all of the previous streams sizes matches to the main Position then data from this stream will be transferred until requested buffer size extends up to the another stream items.
The Seek method implements locating of the TMultiStream Position pointer using the total size of collected stream items:
function TMultiStream.Seek(Offset: Integer; Origin: Word): Longint;
var
len: Integer;
begin
len := GetTotalSize();
case Origin of
soFromBeginning: FPosition := Offset;
soFromCurrent: FPosition := FPosition + Offset;
soFromEnd: FPosition := len - Offset;
end;
if (FPosition len) then
begin
FPosition := len;
end else
if (FPosition begin
FPosition := 0;
end;
Result := FPosition;
end;
Please note that the contents of the TMultiStream can not be modified by calling of Write method. So implementation of this method will be very simple:
function TMultiStream.Write(const Buffer; Count: Integer): Longint;
begin
Result := 0;
end;
Indeed it is quite difficult to imagine such task when you might need to change the contents of the multistream object.
Using of TMultiStream Class
With just few lines of code the new TMultiStream class is ready to use. Lets go ahead and test it:
GetMem(buf, 1024);
Stream := TMultiStream.Create();
try
Stream.AddStream(TStringStream.Create('test string '));
Stream.AddStream(TFileStream.Create('testfile.txt', fmOpenRead));
ZeroMemory(buf, 1024);
Stream.Read(buf^, 1024);
Assert(string(buf) = 'test string test file');
finally
FreeMem(buf);
Stream.Free();
end;
The full source code of classes being used in this article can be downloaded at
multistreamd.zip
This code is constantly being improved and your comments and suggestions are always welcome.
Please write us at info@clevercomponents.com
With best regards,
Sergey S
Clever Components team.
www.clevercomponents.com