home *** CD-ROM | disk | FTP | other *** search
- { 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.
-
-