home *** CD-ROM | disk | FTP | other *** search
/ Chip 2000 October / Chip_2000-10_cd1.bin / zkuste / Delphi / navody / multithread / mchmemorystream.pas < prev    next >
Pascal/Delphi Source File  |  1999-05-10  |  10KB  |  355 lines

  1. { 10-05-1999 10:36:02 PM > [martin on MARTIN] checked out /Reformatting
  2.    according to Delphi guidelines. }
  3. { 06-04-1999 2:42:04 AM > [martin on MARTIN] update: Removed checking
  4.    pragmas Initial Version (0.1) /  }
  5. { 06-04-1999 1:46:37 AM > [martin on MARTIN] check in: (0.0) Initial Version
  6.    / None }
  7. unit MCHMemoryStream;
  8.  
  9. {Martin Harvey 18/7/1998
  10.  
  11. For donkeys years I've had performance problems when reading to
  12. or writing from memory streams in small increments. This unit
  13. intends to fix this problem.
  14.  
  15. Completely rewritten: Martin Harvey 5/4/1999.
  16.  
  17. I was unhappy with some performance issues in the original,
  18. and the scheme for calculating size and position was not very logical,
  19. and had special cases. This is now a more consistent and effecient rewrite}
  20.  
  21. {$RANGECHECKS OFF}
  22. {$STACKCHECKS OFF}
  23. {$IOCHECKS OFF}
  24. {$OVERFLOWCHECKS OFF}
  25.  
  26. interface
  27.  
  28. {This stream acts as a memory stream by storing the data in 4k blocks,
  29.  all of which are attached to a TList}
  30.  
  31. uses Classes;
  32.  
  33. const
  34.   DataBlockSize = 4096;
  35.  
  36. type
  37.   TDataBlockOffset = 0..DataBlockSize - 1;
  38.  
  39.   TDataBlock = array[0..DataBlockSize - 1] of byte;
  40.  
  41.   PDataBlock = ^TDataBlock;
  42.  
  43. {New rules for offset values are as follows:
  44.  
  45.  FPosBlock contains the number of the block which is about to be read or written to,
  46.  given the current position.
  47.  
  48.  FPosOfs contains the offset of the byte that is about to be read or written to,
  49.  given the current position. Always between 0 and DataBlockSize-1
  50.  
  51.  The number of blocks in the stream is given by the list count. If we are at the
  52.  end of the stream, and the size of the stream is an exact multiple of the
  53.  block size, then the last block will be empty.
  54.  
  55.  ie: The last block is never full.
  56.  
  57. }
  58.   TMCHMemoryStream = class(TStream)
  59.   private
  60.     FBlockList:TList;
  61.     FPosBlock:Longint;
  62.     FPosOfs:TDataBlockOffset;
  63.     FLastOfs:TDataBlockOffset;
  64.     {FLastOfs is the offset of the byte to be read or written just off the end of the stream}
  65.   protected
  66.     function GetSize:longint;
  67.     function GetPosition:longint;
  68.     function ConvertOffsetsToLongint(Blocks:longint;BlockOfs:TDataBlockOffset):longint;
  69.     procedure ConvertLongintToOffsets(Input:longint;var Blocks:longint;var BlockOfs:TDataBlockOffset);
  70.     procedure ResizeBlockList(NewNumBlocks:longint);
  71.   public
  72.     constructor Create;
  73.     destructor Destroy;override;
  74.     {Necessary overrides}
  75.     function Read(var Buffer;Count:longint):longint;override;
  76.     function Write(const Buffer;Count:longint):longint;override;
  77.     function Seek(Offset:Longint;Origin:word):Longint;override;
  78.     {Procedures duplicating TCustomMemoryStream functionality}
  79.     procedure SaveToStream(Stream:TStream);
  80.     procedure SaveToFile(const FileName:string);
  81.     {Procedures Duplicating TMemoryStream functionality}
  82.     procedure Clear;
  83.     procedure LoadFromStream(Stream:TStream);
  84.     procedure LoadFromFile(const FileName:string);
  85.     procedure SetSize(NewSize:longint);override;
  86.   end;
  87.  
  88.  
  89. implementation
  90.  
  91. uses SysUtils,Windows;
  92.  
  93. procedure TMCHMemoryStream.ResizeBlockList(NewNumBlocks:longint);
  94.  
  95. var
  96.   iter,CurCount:longint;
  97.   NewBlock:PDataBlock;
  98.  
  99. begin
  100.   CurCount := FBlockList.Count;
  101.   if NewNumBlocks > CurCount then
  102.   begin
  103.     for iter := CurCount to NewNumBlocks - 1 do
  104.     begin
  105.       New(NewBlock);
  106.       FBlockList.Add(NewBlock);
  107.     end;
  108.   end
  109.   else if NewNumBlocks < CurCount then
  110.   begin
  111.     for iter := NewNumBlocks to CurCount - 1 do
  112.     begin
  113.       Dispose(PDataBlock(FBlockList.Items[FBlockList.Count - 1]));
  114.       FBlockList.Delete(FBlockList.Count - 1);
  115.     end;
  116.   end;
  117. end;
  118.  
  119. function TMCHMemoryStream.GetSize;
  120. begin
  121.   result := ConvertOffsetsToLongint(FBlockList.Count - 1,FLastOfs);
  122. end;
  123.  
  124. function TMCHMemoryStream.GetPosition;
  125. begin
  126.   result := ConvertOffsetsToLongint(FPosBlock,FposOfs);
  127. end;
  128.  
  129. function TMCHMemoryStream.ConvertOffsetsTolongint(Blocks:longint;BlockOfs:TDataBlockOffset):longint;
  130. begin
  131.   Result := Blocks * DataBlockSize;
  132.   Result := Result + BlockOfs;
  133. end;
  134.  
  135. procedure TMCHMemoryStream.ConvertLongintToOffsets(Input:longint;var Blocks:longint;var BlockOfs:TDataBlockOffset);
  136. begin
  137.   Blocks := Input div DataBlockSize;
  138.   BlockOfs := Input mod DataBlockSize;
  139. end;
  140.  
  141. procedure TMCHMemoryStream.SetSize(NewSize:longint);
  142.  
  143. var
  144.   NewNumBlocks:longint;
  145.   CurPosition:longint;
  146.  
  147. begin
  148.   if NewSize >= 0 then
  149.   begin
  150.     {Calculate current position}
  151.     CurPosition := GetPosition;
  152.     {Calculate end offsets for new size}
  153.     ConvertLongintToOffsets(NewSize,NewNumBlocks,FLastOfs);
  154.     {Now have the number of blocks needed, and the offset in the last block}
  155.     ResizeBlockList(NewNumBlocks + 1);
  156.     {List resized}
  157.     {Now adjust position vars if needed}
  158.     if NewSize < CurPosition then
  159.     begin
  160.       {Set current position to the end of the stream}
  161.       FPosBlock := NewNumBlocks - 1;
  162.       FPosOfs := FLastOfs;
  163.     end;
  164.   end;
  165. end;
  166.  
  167. procedure TMCHMemoryStream.LoadFromStream(Stream:TStream);
  168.  
  169. var
  170.   TempBlock:TDataBlock;
  171.   BytesThisIteration:longint;
  172.  
  173. begin
  174.   Stream.Seek(0,soFromBeginning);
  175.   repeat
  176.     BytesThisIteration := DataBlockSize;
  177.     if BytesThisIteration > (Stream.Size - Stream.Position) then
  178.       BytesThisIteration := Stream.Size - Stream.Position;
  179.     Stream.ReadBuffer(TempBlock,BytesThisIteration);
  180.     WriteBuffer(TempBlock,BytesThisIteration);
  181.   until Stream.Position = Stream.Size;
  182. end;
  183.  
  184. procedure TMCHMemoryStream.LoadFromFile(const FileName:string);
  185. var
  186.   Stream:TStream;
  187. begin
  188.   Stream := TFileStream.Create(FileName,fmOpenRead);
  189.   try
  190.     LoadFromStream(Stream);
  191.   finally
  192.     Stream.Free;
  193.   end;
  194. end;
  195.  
  196.  
  197. procedure TMCHMemoryStream.SaveToStream(Stream:TStream);
  198.  
  199. var
  200.   TempBlock:TDataBlock;
  201.   BytesThisIteration:longint;
  202.  
  203. begin
  204.   Seek(0,soFromBeginning);
  205.   repeat
  206.     BytesThisIteration := DataBlockSize;
  207.     if BytesThisIteration > (Size - Position) then
  208.       BytesThisIteration := Size - Position;
  209.     ReadBuffer(TempBlock,BytesThisIteration);
  210.     Stream.WriteBuffer(TempBlock,BytesThisIteration);
  211.   until Position = Size;
  212. end;
  213.  
  214. procedure TMCHMemoryStream.SaveToFile(const FileName:string);
  215. var
  216.   Stream:TStream;
  217. begin
  218.   Stream := TFileStream.Create(FileName,fmCreate);
  219.   try
  220.     SaveToStream(Stream);
  221.   finally
  222.     Stream.Free;
  223.   end;
  224. end;
  225.  
  226. function TMCHMemoryStream.Write(const Buffer;Count:longint):longint;
  227.  
  228. var
  229.   CurPos,CurSize,BytesWritten,BytesThisBlock:longint;
  230.   Src:Pointer;
  231.   DestBlock:PDataBlock;
  232.  
  233. begin
  234.   {Returns bytes written}
  235.   if Count < 0 then
  236.   begin
  237.     result := 0;
  238.     exit;
  239.   end;
  240.   result := count;
  241.   CurPos := GetPosition;
  242.   CurSize := GetSize;
  243.   if CurPos + Result > CurSize then
  244.     SetSize(CurPos + Result);
  245.   {Enough blocks allocated, may result in zero sized block at end}
  246.   {Now do the write}
  247.   Src := @Buffer;
  248.   BytesWritten := 0;
  249.   repeat
  250.     DestBlock := PDataBlock(FBlockList.Items[FPosBlock]);
  251.     BytesThisBlock := DataBlockSize - FPosOfs;
  252.     if BytesThisBlock > (Result - BytesWritten) then
  253.       BytesThisBlock := Result - BytesWritten;
  254.     CopyMemory(@DestBlock^[FPosOfs],Src,BytesThisBlock);
  255.     {Now update position vars}
  256.     if BytesThisBlock + FPosOfs = DataBlockSize then
  257.     begin
  258.       FPosOfs := 0;
  259.       Inc(FPosBlock);
  260.     end
  261.     else
  262.       FPosOfs := FPosOfs + BytesThisBlock;
  263.     BytesWritten := BytesWritten + BytesThisBlock;
  264.     Src := Pointer(Integer(Src) + BytesThisBlock);
  265.   until BytesWritten = result;
  266. end;
  267.  
  268. function TMCHMemoryStream.Read(var Buffer;Count:longint):longint;
  269.  
  270. var
  271.   CurPos,CurSize,BytesRead,BytesThisBlock:longint;
  272.   SrcBlock:PDataBlock;
  273.   Dest:pointer;
  274.  
  275.  
  276. begin
  277.   {Returns bytes read}
  278.   CurPos := GetPosition;
  279.   CurSize := GetSize;
  280.   result := Count;
  281.   if result < 0 then result := 0;
  282.   if result > (CurSize - CurPos) then result := CurSize - CurPos;
  283.   if result > 0 then
  284.   begin
  285.     Dest := @Buffer;
  286.     BytesRead := 0;
  287.     repeat
  288.       SrcBlock := PDataBlock(FBlockList.items[FPosBlock]);
  289.       BytesThisBlock := DataBlockSize;
  290.       if FPosBlock = FBlockList.Count - 1 then {We're on the last block}
  291.         BytesThisBlock := FLastOfs;
  292.       BytesThisBlock := BytesThisBlock - FPosOfs;
  293.       if BytesThisBlock > (result - BytesRead) then
  294.         BytesThisBlock := result - BytesRead;
  295.       {Now copy the required number of bytes}
  296.       CopyMemory(Dest,@SrcBlock^[FPosOfs],BytesThisBlock);
  297.       {Now update position state}
  298.       if BytesThisBlock + FPosOfs = DataBlockSize then
  299.       begin
  300.         FPosOfs := 0;
  301.         Inc(FPosBlock);
  302.       end
  303.       else
  304.         FPosOfs := FPosOfs + BytesThisBlock;
  305.       BytesRead := BytesRead + BytesThisBlock;
  306.       Dest := Pointer(Integer(Dest) + BytesThisBlock);
  307.     until BytesRead = result;
  308.   end;
  309. end;
  310.  
  311.  
  312. function TMCHMemoryStream.Seek(Offset:Longint;Origin:word):longint;
  313.  
  314. var
  315.   CurPos,CurSize:longint;
  316.  
  317. begin
  318.   {Remember that it returns new position}
  319.   CurPos := GetPosition;
  320.   CurSize := GetSize;
  321.   case Origin of
  322.     soFromBeginning:result := Offset;
  323.     soFromCurrent:result := CurPos + Offset;
  324.     soFromEnd:result := CurSize - Offset;
  325.   else
  326.     result := CurPos;
  327.   end;
  328.   ConvertLongintToOffsets(result,FPosBlock,FPosOfs);
  329. end;
  330.  
  331. procedure TMCHMemoryStream.Clear;
  332.  
  333. begin
  334.   SetSize(0);
  335. end;
  336.  
  337. destructor TMCHMemoryStream.Destroy;
  338.  
  339. begin
  340.   Clear;
  341.   Dispose(PDataBlock(FBlockList.Items[0]));
  342.   FBlockList.Free;
  343.   inherited Destroy;
  344. end;
  345.  
  346. constructor TMCHMemoryStream.Create;
  347. begin
  348.   inherited Create;
  349.   FBlockList := TList.Create;
  350.   Clear; {Allocates first block}
  351. end;
  352.  
  353. end.
  354.  
  355.