NMFIFOBuffer.pas
上传用户:szzdds
上传日期:2013-09-18
资源大小:293k
文件大小:16k
源码类别:

Delphi控件源码

开发平台:

Delphi

  1. unit NMFifoBuffer;
  2. interface
  3. uses
  4.   Classes, Windows, SysUtils;
  5. type
  6.   TNMFifoBuffer = class( TObject )
  7.   private
  8.     // BufStream
  9.     FBufferSize: LongInt;                       // Current size of the BufStream
  10.     // Memory Buffer
  11.     FMemoryBufferCapacity: LongInt;             // Total Capacity of the memory buffer
  12.     FMemoryBuffer: TMemoryStream;               // Memory stream
  13.     FMemorySize: LongInt;                       // Current size of the memory buffer
  14.     FMemoryAddPosition: LongInt;                // Current add position in memory buffer
  15.     FMemoryRemovePosition: LongInt;             // Current remove position in memory buffer
  16.     FMemoryLow: LongInt;                        // Shuffle when memory drops below this.
  17.     // Disk Buffer
  18.     FFilename: string;                          // Filename of the disk buffer
  19.     FDiskBuffer: TFileStream;                   // File stream for the disk buffer
  20.     FDiskSize: LongInt;                         // Current size of the disk buffer
  21.     FDiskAddPosition: LongInt;                  // Current add position in disk buffer
  22.     FDiskRemovePosition: LongInt;               // Current remove position in disk buffer
  23.     FThreadRunning: Boolean;                    // Shuffle thread is running
  24.     function _CreateTemporaryFileName: string;
  25.     procedure _Init;
  26.     procedure InitDiskBuffer;
  27.     function _CalculateThreshold( const PercentMemory: Real ): LongInt;
  28.     procedure _Shuffle( Data: Pointer );
  29.     procedure _SetMemoryBufferCapacity( const NewCapacity: LongInt );
  30.   protected
  31.     property ThreadRunning: Boolean read FThreadRunning;
  32.   public
  33.     constructor Create;
  34.     destructor Destroy; override;
  35.     procedure Clear;
  36.     procedure LoadFromStream( Stream: TStream );
  37.     procedure LoadFromFile( const FileName: string );
  38.     function Append( const Buffer: Pointer; const Count: LongInt ): LongInt;
  39.     function Remove( Buffer: Pointer; const Count: LongInt ): LongInt;
  40.     function Peek( Buffer: Pointer; const Count: LongInt ): LongInt;
  41.     function Search( const Substr: Pointer ): LongInt;
  42.     property BufferSize: Longint read FBufferSize;
  43.     property MemorySize: Longint read FMemorySize;
  44.     property DiskSize: Longint read FDiskSize;
  45.   published
  46.     property MemoryBufferCapacity: Longint read FMemoryBufferCapacity write _SetMemoryBufferCapacity;
  47.     property MemoryLow: LongInt read FMemoryLow write FMemoryLow;
  48.   end;
  49. implementation
  50. uses
  51.   PSock;
  52. // ***************************************************************************
  53. //TNMFifoBuffer
  54. constructor TNMFifoBuffer.Create;
  55. begin
  56.   inherited Create;
  57.   FMemoryBufferCapacity := _CalculateThreshold( 0.05 ); // Init Threshold to 5% of free memory!
  58.   _Init;
  59. end;
  60. destructor TNMFifoBuffer.Destroy;
  61. begin
  62.   if FDiskBuffer <> nil then
  63.     begin
  64.       FDiskBuffer.Free;
  65.       DeleteFile( FFilename );
  66.     end;
  67.   FMemoryBuffer.Free;
  68.   inherited;
  69. end;
  70. // Support routines.
  71. // ***************************************************************************
  72. function TNMFifoBuffer.Remove( Buffer: Pointer; const Count: LongInt ): LongInt;
  73. var
  74.   tmp                 : LongInt;
  75. begin
  76.   while FThreadRunning do
  77.     Sleep( 50 );
  78.   Result := 0;
  79.   if ( Count > 0 ) and ( FBufferSize > 0 ) then
  80.     begin
  81.       if IsBadWritePtr( PByteArray( Buffer ), Count ) then
  82.         raise Exception.Create( 'Can''t write to the destination buffer' );
  83.       tmp := Count;
  84.       if FMemoryRemovePosition + Count > FMemoryBufferCapacity then
  85.         tmp := FMemoryBufferCapacity - FMemoryRemovePosition;
  86.       FMemorybuffer.Position := FMemoryRemovePosition;
  87.       if tmp > 0 then
  88.         begin
  89.           Result := FMemorybuffer.Read( Buffer^, tmp );
  90.           dec( FMemorySize, Result );
  91.           inc( FMemoryRemovePosition, Result );
  92.         end
  93.       else
  94.         Result := 0;
  95.       tmp := 0;
  96.       if FDiskBuffer <> nil then
  97.         begin
  98.           if Result < Count then
  99.             begin
  100.               FDiskBuffer.Position := FDiskRemovePosition;
  101.               tmp := FDiskBuffer.Read( Pointer( Longint( Buffer ) + Result )^, Count - Result );
  102.               Dec( FDiskSize, tmp );
  103.               inc( FDiskRemovePosition, tmp );
  104.             end;
  105.         end;
  106.       Result := Result + tmp;
  107.       dec( FBufferSize, Result );
  108.       if FMemorySize = 0 then
  109.         begin
  110.           FMemoryAddPosition := 0;
  111.           FMemoryRemovePosition := 0;
  112.           if FDiskSize > 0 then
  113.             begin
  114.               FThreadRunning := True;
  115.               ExecuteInThread( _Shuffle, nil );
  116.             end;
  117.         end;
  118.     end;
  119. end;
  120. function TNMFifoBuffer.Peek( Buffer: Pointer; const Count: LongInt ): LongInt;
  121. var
  122.   tmp                 : LongInt;
  123. begin
  124.   while FThreadRunning do
  125.     Sleep( 50 );
  126.   Result := 0;
  127.   if ( Count > 0 ) and ( FBufferSize > 0 ) then
  128.     begin
  129.       if IsBadWritePtr( PByteArray( Buffer ), Count ) then
  130.         raise Exception.Create( 'Can''t write to the destination buffer' );
  131.       tmp := Count;
  132.       if FMemoryRemovePosition + Count > FMemoryBufferCapacity then
  133.         tmp := FMemoryBufferCapacity - FMemoryRemovePosition;
  134.       FMemorybuffer.Position := FMemoryRemovePosition;
  135.       Result := FMemorybuffer.Read( Buffer^, tmp );
  136.       tmp := 0;
  137.       if FDiskBuffer <> nil then
  138.         begin
  139.           if Result < Count then
  140.             begin
  141.               FDiskBuffer.Position := FDiskRemovePosition;
  142.               tmp := FDiskBuffer.Read( Pointer( Longint( Buffer ) + Result )^, Count - Result );
  143.             end;
  144.         end;
  145.       Result := Result + tmp;
  146.     end;
  147. end;
  148. function TNMFifoBuffer.Search( const Substr: Pointer ): LongInt;
  149. var
  150.   Ptr, Ptr1           : PChar;
  151.   SubLen              : LongInt;
  152.   function InternalSearch: LongInt;
  153.   var
  154.     Sav1, Sav2        : PChar;
  155.   begin
  156.     Sav1 := Ptr;
  157.     Sav2 := StrPos( Ptr, Ptr1 );
  158.     Result := LongInt( Sav2 - Sav1 );
  159.     inc( Result, SubLen );
  160.     if Result < 0 then
  161.       Result := 0;
  162.   end;
  163. const
  164.   MaxBufSize          = $FFFF;
  165. var
  166.   Buffer              : PChar;
  167.   DiskPosition, DiskSize, Count, FoundAt, A1, A2: LongInt;
  168. begin
  169.   Result := 0;
  170.   SubLen := StrLen( SubStr );
  171.   // we must have something in the buffer and
  172.   // the search must be less or equal the buffer size.
  173.   if ( FMemorySize > 0 ) then
  174.     if ( SubLen > FBufferSize ) or ( SubLen > MaxBufSize ) then
  175.       Result := -1
  176.     else
  177.       begin
  178.         Ptr1 := SubStr;
  179.         while FThreadRunning do
  180.           Sleep( 50 );
  181.         Ptr := Pointer( FMemoryBuffer.Memory );
  182.         inc( Ptr, FMemoryRemovePosition );
  183.         FoundAt := 0;
  184.         if SubLen <= FMemorySize then
  185.           FoundAt := InternalSearch;
  186.         if FoundAt > FMemorySize then
  187.           FoundAt := 0;
  188.         if FoundAt = 0 then
  189.           begin
  190.             if FDiskSize > 0 then
  191.               begin
  192.                 DiskPosition := 0;
  193.                 DiskSize := FDiskSize;
  194.                 Buffer := AllocMem( MaxBufSize );
  195.                 try
  196.                   // take care of the possibility that the substr spans both buffers
  197.                   if subLen = FBufferSize then  // Exact match entire buffer
  198.                     begin
  199.                       A1 := FMemorySize;
  200.                       A2 := FDiskSize;
  201.                     end
  202.                   else if SubLen > FMemorySize then // all of memory and part of disk
  203.                     begin
  204.                       A1 := FMemorySize;
  205.                       A2 := SubLen - 1;
  206.                     end
  207.                   else
  208.                     begin                       // part of memory and part of disk
  209.                       A1 := SubLen - 1;
  210.                       A2 := SubLen - 1;
  211.                     end;
  212.                   move( Pointer( LongInt( FMemoryBuffer.Memory ) + FMemoryBufferCapacity - A1 )^, Buffer^, A1 );
  213.                   FDiskBuffer.Position := DiskPosition + FDiskRemovePosition;
  214.                   FDiskBuffer.Read( Pointer( Longint( Buffer ) + A1 )^, A2 );
  215.                   Ptr := Buffer;
  216.                   FoundAt := InternalSearch;
  217.                   if FoundAt > 0 then
  218.                     begin
  219.                       if ( subLen <= FMemorySize ) then
  220.                         FoundAt := FMemorySize + FoundAt - A1;
  221.                     end
  222.                   else
  223.                     begin
  224.                       if SubLen <= FDiskSize then
  225.                         begin
  226.                           Count := 0;
  227.                           while ( FoundAt = 0 ) and ( DiskSize > 0 ) do
  228.                             begin
  229.                               inc( DiskPosition, Count );
  230.                               FDiskBuffer.Position := DiskPosition + FDiskRemovePosition;
  231.                               Count := FDiskBuffer.Read( Buffer^, MaxBufSize );
  232.                               if Count <= 0 then
  233.                                 break;
  234.                               Ptr := Buffer;
  235.                               FoundAt := InternalSearch;
  236.                             end;
  237.                           if FoundAt > 0 then
  238.                             FoundAt := FMemorySize + DiskPosition + FoundAt;
  239.                         end;
  240.                     end;
  241.                 finally
  242.                   FreeMem( Buffer, MaxBufSize );
  243.                 end;
  244.               end;
  245.           end;
  246.         Result := FoundAt;
  247.       end;
  248. end;
  249. function TNMFifoBuffer.Append( const Buffer: Pointer; const Count: LongInt ): LongInt;
  250. begin
  251.   while FThreadRunning do
  252.     Sleep( 50 );
  253.   Result := 0;
  254.   if Count > 0 then
  255.     begin
  256.       if IsBadReadPtr( PByteArray( Buffer ), Count ) then
  257.         raise Exception.Create( 'Can''t read the source buffer' );
  258.       Result := Count;
  259.       if FMemoryAddPosition + Count > FMemoryBufferCapacity then
  260.         Result := FMemoryBufferCapacity - FMemoryAddPosition;
  261.       FMemoryBuffer.Position := FMemoryAddPosition;
  262.       Result := FMemoryBuffer.Write( Buffer^, Result );
  263.       inc( FMemorySize, Result );
  264.       inc( FMemoryAddPosition, Result );
  265.       inc( FBufferSize, Result );
  266.       if Result < Count then
  267.         begin
  268.           if FDiskBuffer = nil then
  269.             InitDiskBuffer;
  270.           FDiskBuffer.Position := FDiskAddPosition;
  271.           Result := FDiskBuffer.Write( Pointer( Longint( Buffer ) + Result )^, Count - Result );
  272.           inc( FDiskSize, Result );
  273.           inc( FDiskAddPosition, Result );
  274.           inc( FBufferSize, Result );
  275.         end;
  276.     end;
  277. end;
  278. procedure TNMFifoBuffer._Init;
  279. var
  280.   Zero                : string;
  281. begin
  282.   FDiskSize := 0;                               // Current size of the disk buffer
  283.   FDiskAddPosition := 0;                        // Current add position in disk buffer
  284.   FDiskRemovePosition := 0;                     // Current remove position in disk buffer
  285.   FBufferSize := 0;                             // Current size of the BufStream
  286.   FMemoryBuffer := TMemoryStream.Create;        // Memory stream
  287.   FMemoryBuffer.Size := FMemoryBufferCapacity;
  288.   FMemoryBuffer.Position := FMemoryBuffer.Size;
  289.   Zero := #0;
  290.   FMemoryBuffer.Write( PChar( Zero )^, 1 );     // Put a zero at the end of the memory buffer
  291.   FMemorySize := 0;                             // Current size of the memory buffer
  292.   FMemoryAddPosition := 0;                      // Current add position in memory buffer
  293.   FMemoryRemovePosition := 0;                   // Current remove position in memory buffer
  294.   FMemoryLow := 0;                              // Shuffle when memory drops below this.
  295.   FThreadRunning := False;                      // Shuffle thread is running
  296. end;
  297. procedure TNMFifobuffer.InitDiskBuffer;
  298. begin
  299.   FFilename := _CreateTemporaryFileName;        // Filename of the disk buffer
  300.   FDiskBuffer := TFileStream.Create( FFilename, fmOpenReadWrite or fmShareExclusive ); // File stream for the disk buffer
  301.   FDiskBuffer.Size := 1024;                     // pre-allocate a small disk file size.
  302.   FDiskSize := 0;                               // Current size of the disk buffer
  303.   FDiskAddPosition := 0;                        // Current add position in disk buffer
  304.   FDiskRemovePosition := 0;                     // Current remove position in disk buffer
  305. end;
  306. procedure TNMFifoBuffer._SetMemoryBufferCapacity( const NewCapacity: LongInt );
  307. begin
  308.   if NewCapacity < FMemorysize then
  309.     raise Exception.Create( 'Can not lower memory capacity at this time' );
  310.   FMemoryBuffer.SetSize( NewCapacity );
  311.   FMemoryBufferCapacity := NewCapacity;
  312. end;
  313. function TNMFifoBuffer._CalculateThreshold( const PercentMemory: Real ): LongInt;
  314. const
  315.   MinCapacity         = 64 * 1024;
  316. var
  317.   MS                  : TMemoryStatus;
  318. begin
  319.   MS.dwLength := SizeOf( TMemoryStatus );
  320.   GlobalMemoryStatus( MS );
  321.   Result := Trunc( MS.dwAvailPhys * PercentMemory ) - ( Trunc( MS.dwAvailPhys * PercentMemory ) mod 1024 );
  322.   if Result < MinCapacity then
  323.     Result := MinCapacity;
  324. end;
  325. function TNMFifoBuffer._CreateTemporaryFileName: string;
  326. var
  327.   nBufferLength       : DWORD;
  328.   lpPathName, lpTempFileName: PChar;
  329. begin
  330.   Result := '';
  331.   lpPathName := nil;
  332.   lpTempFileName := nil;
  333.   // first get the length of the tempory path
  334.   nBufferLength := GetTempPath( 0, lpPathName );
  335.   Win32Check( BOOL( nBufferLength ) );
  336.   // Allocate a buffer of the specified length + 1
  337.   lpPathName := AllocMem( nBufferLength );
  338.   try
  339.     // Get the tempory path
  340.     Win32Check( BOOL( GetTempPath( nBufferLength, lpPathName ) ) );
  341.     // Increase the tempory path to hold the file name also.
  342.     lpTempFileName := AllocMem( 256 );
  343.     try
  344.       // Get the temporary file name
  345.       Win32Check( BOOL( GetTempFileName( lpPathName, PChar( 'Buf' ), 0, lpTempFileName ) ) );
  346.       // return the file name and path
  347.       SetString( Result, lpTempFileName, StrLen( lpTempFileName ) );
  348.       // Lastly free the buffers.
  349.     finally
  350.       FreeMem( lpPathName );
  351.     end;
  352.   finally
  353.     FreeMem( lpTempFileName );
  354.   end;
  355.   if Result = '' then
  356.     raise Exception.Create( 'Can''t create a temporary file' );
  357. end;
  358. procedure TNMFifoBuffer._Shuffle( Data: Pointer );
  359. var
  360.   tmp                 : LongInt;
  361.   Size                : LongInt;
  362. begin
  363.   if FDiskBuffer <> nil then
  364.     begin
  365.       FDiskBuffer.Position := FDiskRemovePosition;
  366.       if FDiskSize < FMemoryBufferCapacity then
  367.         Size := FDiskSize
  368.       else
  369.         Size := FMemoryBufferCapacity;
  370.       tmp := FDiskBuffer.Read( FMemoryBuffer.Memory^, Size );
  371.       FMemorySize := tmp;
  372.       FMemoryAddPosition := tmp;
  373.       FMemoryRemovePosition := 0;
  374.       Dec( FDiskSize, tmp );
  375.       if FDiskSize > 0 then
  376.         inc( FDiskRemovePosition, tmp )
  377.       else
  378.         begin
  379.           FDiskAddPosition := 0;
  380.           FDiskRemovePosition := 0;
  381.         end;
  382.     end;
  383.   FThreadRunning := False;
  384. end;
  385. procedure TNMFifoBuffer.Clear;
  386. begin
  387.   while FThreadRunning do
  388.     Sleep( 50 );
  389.   if FDiskBuffer <> nil then
  390.     begin
  391.       FDiskBuffer.Free;
  392.       DeleteFile( FFilename );
  393.     end;
  394.   FMemoryBuffer.Free;
  395.   _Init;
  396. end;
  397. procedure TNMFifoBuffer.LoadFromStream( Stream: TStream );
  398. var
  399.   Count               : Longint;
  400. begin
  401.   Stream.Position := 0;
  402.   Count := Stream.Size;
  403.   if Count > 0 then
  404.     if Count <= FMemoryBufferCapacity then
  405.       begin
  406.         FMemoryBuffer.LoadFromStream( Stream );
  407.         FBufferSize := Count;
  408.         FMemorySize := Count;
  409.         FMemoryAddPosition := Count;
  410.         FMemoryRemovePosition := 0;
  411.         FDiskSize := 0;
  412.         FDiskAddPosition := 0;
  413.         FDiskRemovePosition := 0;
  414.       end
  415.     else
  416.       begin
  417.         if FDiskBuffer = nil then
  418.           InitDiskBuffer;
  419.         FDiskBuffer.CopyFrom( Stream, Count );
  420.         FBufferSize := Count;
  421.         FMemorySize := 0;
  422.         FMemoryAddPosition := FMemoryBufferCapacity; // set to max so shuffle can shuffle
  423.         FMemoryRemovePosition := FMemoryBufferCapacity; // set to max so shuffle can shuffle
  424.         FDiskSize := Count;
  425.         FDiskAddPosition := Count;
  426.         FDiskRemovePosition := 0;
  427.         FThreadRunning := True;                 // do this here so that thread initialization is counted as thread running
  428.         ExecuteInThread( _Shuffle, nil );
  429.       end;
  430. end;
  431. procedure TNMFifoBuffer.LoadFromFile( const FileName: string );
  432. var
  433.   Stream              : TStream;
  434. begin
  435.   Stream := TFileStream.Create( FileName, fmOpenRead or fmShareDenyWrite );
  436.   try
  437.     LoadFromStream( Stream );
  438.   finally
  439.     Stream.Free;
  440.   end;
  441. end;
  442. end.