home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2000 October
/
Chip_2000-10_cd1.bin
/
zkuste
/
Delphi
/
navody
/
multithread
/
mchmemorystream.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
1999-05-10
|
10KB
|
355 lines
{ 10-05-1999 10:36:02 PM > [martin on MARTIN] checked out /Reformatting
according to Delphi guidelines. }
{ 06-04-1999 2:42:04 AM > [martin on MARTIN] update: Removed checking
pragmas Initial Version (0.1) / }
{ 06-04-1999 1:46:37 AM > [martin on MARTIN] check in: (0.0) Initial Version
/ None }
unit MCHMemoryStream;
{Martin Harvey 18/7/1998
For donkeys years I've had performance problems when reading to
or writing from memory streams in small increments. This unit
intends to fix this problem.
Completely rewritten: Martin Harvey 5/4/1999.
I was unhappy with some performance issues in the original,
and the scheme for calculating size and position was not very logical,
and had special cases. This is now a more consistent and effecient rewrite}
{$RANGECHECKS OFF}
{$STACKCHECKS OFF}
{$IOCHECKS OFF}
{$OVERFLOWCHECKS OFF}
interface
{This stream acts as a memory stream by storing the data in 4k blocks,
all of which are attached to a TList}
uses Classes;
const
DataBlockSize = 4096;
type
TDataBlockOffset = 0..DataBlockSize - 1;
TDataBlock = array[0..DataBlockSize - 1] of byte;
PDataBlock = ^TDataBlock;
{New rules for offset values are as follows:
FPosBlock contains the number of the block which is about to be read or written to,
given the current position.
FPosOfs contains the offset of the byte that is about to be read or written to,
given the current position. Always between 0 and DataBlockSize-1
The number of blocks in the stream is given by the list count. If we are at the
end of the stream, and the size of the stream is an exact multiple of the
block size, then the last block will be empty.
ie: The last block is never full.
}
TMCHMemoryStream = class(TStream)
private
FBlockList:TList;
FPosBlock:Longint;
FPosOfs:TDataBlockOffset;
FLastOfs:TDataBlockOffset;
{FLastOfs is the offset of the byte to be read or written just off the end of the stream}
protected
function GetSize:longint;
function GetPosition:longint;
function ConvertOffsetsToLongint(Blocks:longint;BlockOfs:TDataBlockOffset):longint;
procedure ConvertLongintToOffsets(Input:longint;var Blocks:longint;var BlockOfs:TDataBlockOffset);
procedure ResizeBlockList(NewNumBlocks:longint);
public
constructor Create;
destructor Destroy;override;
{Necessary overrides}
function Read(var Buffer;Count:longint):longint;override;
function Write(const Buffer;Count:longint):longint;override;
function Seek(Offset:Longint;Origin:word):Longint;override;
{Procedures duplicating TCustomMemoryStream functionality}
procedure SaveToStream(Stream:TStream);
procedure SaveToFile(const FileName:string);
{Procedures Duplicating TMemoryStream functionality}
procedure Clear;
procedure LoadFromStream(Stream:TStream);
procedure LoadFromFile(const FileName:string);
procedure SetSize(NewSize:longint);override;
end;
implementation
uses SysUtils,Windows;
procedure TMCHMemoryStream.ResizeBlockList(NewNumBlocks:longint);
var
iter,CurCount:longint;
NewBlock:PDataBlock;
begin
CurCount := FBlockList.Count;
if NewNumBlocks > CurCount then
begin
for iter := CurCount to NewNumBlocks - 1 do
begin
New(NewBlock);
FBlockList.Add(NewBlock);
end;
end
else if NewNumBlocks < CurCount then
begin
for iter := NewNumBlocks to CurCount - 1 do
begin
Dispose(PDataBlock(FBlockList.Items[FBlockList.Count - 1]));
FBlockList.Delete(FBlockList.Count - 1);
end;
end;
end;
function TMCHMemoryStream.GetSize;
begin
result := ConvertOffsetsToLongint(FBlockList.Count - 1,FLastOfs);
end;
function TMCHMemoryStream.GetPosition;
begin
result := ConvertOffsetsToLongint(FPosBlock,FposOfs);
end;
function TMCHMemoryStream.ConvertOffsetsTolongint(Blocks:longint;BlockOfs:TDataBlockOffset):longint;
begin
Result := Blocks * DataBlockSize;
Result := Result + BlockOfs;
end;
procedure TMCHMemoryStream.ConvertLongintToOffsets(Input:longint;var Blocks:longint;var BlockOfs:TDataBlockOffset);
begin
Blocks := Input div DataBlockSize;
BlockOfs := Input mod DataBlockSize;
end;
procedure TMCHMemoryStream.SetSize(NewSize:longint);
var
NewNumBlocks:longint;
CurPosition:longint;
begin
if NewSize >= 0 then
begin
{Calculate current position}
CurPosition := GetPosition;
{Calculate end offsets for new size}
ConvertLongintToOffsets(NewSize,NewNumBlocks,FLastOfs);
{Now have the number of blocks needed, and the offset in the last block}
ResizeBlockList(NewNumBlocks + 1);
{List resized}
{Now adjust position vars if needed}
if NewSize < CurPosition then
begin
{Set current position to the end of the stream}
FPosBlock := NewNumBlocks - 1;
FPosOfs := FLastOfs;
end;
end;
end;
procedure TMCHMemoryStream.LoadFromStream(Stream:TStream);
var
TempBlock:TDataBlock;
BytesThisIteration:longint;
begin
Stream.Seek(0,soFromBeginning);
repeat
BytesThisIteration := DataBlockSize;
if BytesThisIteration > (Stream.Size - Stream.Position) then
BytesThisIteration := Stream.Size - Stream.Position;
Stream.ReadBuffer(TempBlock,BytesThisIteration);
WriteBuffer(TempBlock,BytesThisIteration);
until Stream.Position = Stream.Size;
end;
procedure TMCHMemoryStream.LoadFromFile(const FileName:string);
var
Stream:TStream;
begin
Stream := TFileStream.Create(FileName,fmOpenRead);
try
LoadFromStream(Stream);
finally
Stream.Free;
end;
end;
procedure TMCHMemoryStream.SaveToStream(Stream:TStream);
var
TempBlock:TDataBlock;
BytesThisIteration:longint;
begin
Seek(0,soFromBeginning);
repeat
BytesThisIteration := DataBlockSize;
if BytesThisIteration > (Size - Position) then
BytesThisIteration := Size - Position;
ReadBuffer(TempBlock,BytesThisIteration);
Stream.WriteBuffer(TempBlock,BytesThisIteration);
until Position = Size;
end;
procedure TMCHMemoryStream.SaveToFile(const FileName:string);
var
Stream:TStream;
begin
Stream := TFileStream.Create(FileName,fmCreate);
try
SaveToStream(Stream);
finally
Stream.Free;
end;
end;
function TMCHMemoryStream.Write(const Buffer;Count:longint):longint;
var
CurPos,CurSize,BytesWritten,BytesThisBlock:longint;
Src:Pointer;
DestBlock:PDataBlock;
begin
{Returns bytes written}
if Count < 0 then
begin
result := 0;
exit;
end;
result := count;
CurPos := GetPosition;
CurSize := GetSize;
if CurPos + Result > CurSize then
SetSize(CurPos + Result);
{Enough blocks allocated, may result in zero sized block at end}
{Now do the write}
Src := @Buffer;
BytesWritten := 0;
repeat
DestBlock := PDataBlock(FBlockList.Items[FPosBlock]);
BytesThisBlock := DataBlockSize - FPosOfs;
if BytesThisBlock > (Result - BytesWritten) then
BytesThisBlock := Result - BytesWritten;
CopyMemory(@DestBlock^[FPosOfs],Src,BytesThisBlock);
{Now update position vars}
if BytesThisBlock + FPosOfs = DataBlockSize then
begin
FPosOfs := 0;
Inc(FPosBlock);
end
else
FPosOfs := FPosOfs + BytesThisBlock;
BytesWritten := BytesWritten + BytesThisBlock;
Src := Pointer(Integer(Src) + BytesThisBlock);
until BytesWritten = result;
end;
function TMCHMemoryStream.Read(var Buffer;Count:longint):longint;
var
CurPos,CurSize,BytesRead,BytesThisBlock:longint;
SrcBlock:PDataBlock;
Dest:pointer;
begin
{Returns bytes read}
CurPos := GetPosition;
CurSize := GetSize;
result := Count;
if result < 0 then result := 0;
if result > (CurSize - CurPos) then result := CurSize - CurPos;
if result > 0 then
begin
Dest := @Buffer;
BytesRead := 0;
repeat
SrcBlock := PDataBlock(FBlockList.items[FPosBlock]);
BytesThisBlock := DataBlockSize;
if FPosBlock = FBlockList.Count - 1 then {We're on the last block}
BytesThisBlock := FLastOfs;
BytesThisBlock := BytesThisBlock - FPosOfs;
if BytesThisBlock > (result - BytesRead) then
BytesThisBlock := result - BytesRead;
{Now copy the required number of bytes}
CopyMemory(Dest,@SrcBlock^[FPosOfs],BytesThisBlock);
{Now update position state}
if BytesThisBlock + FPosOfs = DataBlockSize then
begin
FPosOfs := 0;
Inc(FPosBlock);
end
else
FPosOfs := FPosOfs + BytesThisBlock;
BytesRead := BytesRead + BytesThisBlock;
Dest := Pointer(Integer(Dest) + BytesThisBlock);
until BytesRead = result;
end;
end;
function TMCHMemoryStream.Seek(Offset:Longint;Origin:word):longint;
var
CurPos,CurSize:longint;
begin
{Remember that it returns new position}
CurPos := GetPosition;
CurSize := GetSize;
case Origin of
soFromBeginning:result := Offset;
soFromCurrent:result := CurPos + Offset;
soFromEnd:result := CurSize - Offset;
else
result := CurPos;
end;
ConvertLongintToOffsets(result,FPosBlock,FPosOfs);
end;
procedure TMCHMemoryStream.Clear;
begin
SetSize(0);
end;
destructor TMCHMemoryStream.Destroy;
begin
Clear;
Dispose(PDataBlock(FBlockList.Items[0]));
FBlockList.Free;
inherited Destroy;
end;
constructor TMCHMemoryStream.Create;
begin
inherited Create;
FBlockList := TList.Create;
Clear; {Allocates first block}
end;
end.