Guest User

suStreamsUnit

a guest
Nov 19th, 2014
365
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Delphi 25.91 KB | None | 0 0
  1. {
  2.   Модуль содержит различные классы, для работы с потоками.
  3. }
  4. unit suStreamsUnit;
  5. {$WEAKLINKRTTI ON}
  6. {$RTTI EXPLICIT METHODS([]) PROPERTIES([]) FIELDS([])}
  7.  
  8. interface
  9.  
  10. {$I CompilerDefines.inc}
  11.  
  12. uses
  13. {$IFDEF VCL_XE2_OR_ABOVE}
  14.   {$IFDEF MSWINDOWS}
  15.     Winapi.Windows, Winapi.ActiveX, System.Win.ComObj, Vcl.AxCtrls,
  16.   {$ENDIF}
  17.   {$IFDEF POSIX}
  18.     System.Types, suComHelpersUnit.Posix,
  19.   {$ENDIF POSIX}
  20.  
  21.   System.Classes, System.SysUtils, System.Math, System.RTLConsts,
  22. {$ELSE}
  23.   Windows, Classes, SysUtils, ActiveX, AxCtrls, ComObj, Math, RTLConsts,
  24. {$ENDIF}
  25.   Generics.Collections,
  26.  
  27.   suMiscCrossPlatformUtilsUnit;
  28.  
  29. type
  30.   //Поток получает ссылку на инициализированный участок памяти и работает с ним как обычный
  31.   //MemoryStream
  32.   TsuInMemoryStream = class(TCustomMemoryStream)
  33.   private
  34.     FOwned: Boolean;
  35.   public
  36.     constructor Create(Memory: Pointer; const Size: Int64; AOwned: Boolean = False);
  37.     destructor Destroy; override;
  38.  
  39.     function Write(const Buffer; Count: Longint): Longint; override;
  40.  
  41.   end;
  42.  
  43.   //Исправленный TStreamAdapter
  44.   TsuStreamAdapter = class(TStreamAdapter)
  45.   public
  46.     function Seek(dlibMove: Int64; dwOrigin: Integer;
  47.       out libNewPosition: Int64): HRESULT; override; stdcall;
  48.   end;
  49.  
  50.   //Исправленный TOleStream
  51.   TsuOleStream = class(TOleStream)
  52.   public
  53.     function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;
  54.  
  55.     property OwnerStream: IStream read GetIStream;
  56.   end;
  57.  
  58.   //Класс, работает с частью стрима как со стримом.
  59.   TsuPartStream = class(TStream)
  60.   private
  61.     FStream: TStream;
  62.     FPartSize: Int64;
  63.     FPosition: Int64;
  64.     FIsOwner: Boolean;
  65.     FPartOffset: Int64;
  66.   public
  67.     //Стрим для работы с куском стрима
  68.     //@param(AStream стрим)
  69.     //@param(APartOffset смещение в стриме)
  70.     //@param(APartSize размер куска после смещения)
  71.     //@param(IsOwner самостоятельно уничтожить стрим
  72.     constructor Create(AStream: TStream; const APartOffset, APartSize: Int64; IsOwner: Boolean = False);
  73.     destructor Destroy; override;
  74.  
  75.     function Read(var Buffer; Count: Longint): Longint; override;
  76.     function Write(const Buffer; Count: Integer): Longint; override;
  77.     function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;
  78.  
  79.     //Смещение в стриме
  80.     property PartOffset: Int64 read FPartOffset;
  81.   end;
  82.  
  83.   //Класс, работает с частью истрима как со истримом.
  84.   TsuPartIStream = class(TInterfacedObject, IStream)
  85.   private
  86.     FStream: IStream;
  87.     FPartSize: Int64;
  88.     FPosition: Int64;
  89.     FPartOffset: Int64;
  90.   public
  91.     //Стрим для работы с куском стрима
  92.     //@param(AStream стрим)
  93.     //@param(APartOffset смещение в стриме)
  94.     //@param(APartSize размер куска после смещения)
  95.     constructor Create(AStream: IStream; const APartOffset, APartSize: Int64);
  96.  
  97.     function Read(pv: Pointer; cb: Longint; pcbRead: PLongint): HResult; stdcall;
  98.     function Write(pv: Pointer; cb: Longint; pcbWritten: PLongint): HResult; stdcall;
  99.     function Seek(dlibMove: Largeint; dwOrigin: Longint; out libNewPosition: Largeint): HResult; stdcall;
  100.  
  101.     function SetSize(libNewSize: Largeint): HResult; stdcall;
  102.     function CopyTo(stm: IStream; cb: Largeint; out cbRead: Largeint;
  103.       out cbWritten: Largeint): HResult; stdcall;
  104.     function Commit(grfCommitFlags: Longint): HResult; stdcall;
  105.     function Revert: HResult; stdcall;
  106.     function LockRegion(libOffset: Largeint; cb: Largeint;
  107.       dwLockType: Longint): HResult; stdcall;
  108.     function UnlockRegion(libOffset: Largeint; cb: Largeint;
  109.       dwLockType: Longint): HResult; stdcall;
  110.     function Stat(out statstg: TStatStg;
  111.       grfStatFlag: Longint): HResult; stdcall;
  112.     function Clone(out stm: IStream): HResult; stdcall;
  113.  
  114.     //Смещение в стриме
  115.     property PartOffset: Int64 read FPartOffset;
  116.   end;
  117.  
  118.   TsuIFileStream = class(TsuStreamAdapter)
  119.   private
  120.     FMode: Word;
  121.   public
  122.     constructor Create(const AFileName: string; Mode: Word);
  123.     function Clone(out stm: IStream): HRESULT; override; stdcall;
  124.   end;
  125.  
  126.   TsuIFileMemoryStream = class(TsuStreamAdapter)
  127.   private
  128.     FFileName: string;
  129.   public
  130.     constructor Create(const AFileName: string);
  131.     function Clone(out stm: IStream): HRESULT; override; stdcall;
  132.   end;
  133.  
  134.   TsuResourceStreamAdapter = class(TsuStreamAdapter)
  135.   private
  136.     FResName: string;
  137.     FInstance: THandle;
  138.   public
  139.     constructor Create(Stream: TResourceStream; Ownership: TStreamOwnership; AInstance: THandle; const AResName: string);
  140.  
  141.     function Clone(out stm: IStream): HResult; override; stdcall;
  142.   end;
  143.  
  144.   //Базовый класс для создания прокси к стримам
  145.   TsuProxyStream = class(TStream)
  146.   private
  147.     FStream: TStream;
  148.     FOwnership: TStreamOwnership;
  149.   public
  150.     constructor Create(AStream: TStream; Ownership: TStreamOwnership = soOwned);
  151.     destructor Destroy; override;
  152.  
  153.     function Read(var Buffer; Count: Longint): Longint; override;
  154.     function Write(const Buffer; Count: Longint): Longint; override;
  155.     function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;
  156.  
  157.     property Stream: TStream read FStream;
  158.   end;
  159.  
  160.   TsuReadOnlyProxyStream = class(TsuProxyStream)
  161.   public
  162.     function Write(const Buffer; Count: Longint): Longint; override;
  163.   end;
  164.  
  165.  
  166.   {
  167.      Стрим кэшируючий чтение раннее прочитанных блоков. Сделан для оптимизации
  168.      чтения mp4 файлов флэшом.
  169.  
  170.      Было проанализировано чтение стрима флэшом, и выяснилось что в за время
  171.      просмотра ролика, чтение данных из файла превосходит размер самого файла
  172.      от 2х до 6ти.
  173.  
  174.      Соответсвенно была построена карта доступа к файлу, и после проведения тестов,
  175.      оказалось, что стрим читается до 6ти раз дольше чем если бы было прямое
  176.      чтение.
  177.  
  178.      Благодаря данному классу удалось ускорить чтение по карте доступа от 30 до 50%.
  179.  
  180.      Для наглядности пример карта доступа:
  181.       Позиция, Кол-во прочитанных данных
  182.       24515,16384
  183.       9338614,16384
  184.       9339160,16384
  185.       24523,16384
  186.       9347688,16384
  187.       37111,16384
  188.       25192,16384
  189.       41576,16384
  190.       37394,16384
  191.       53778,16384
  192.       37421,16384
  193.       53809,16384
  194.  
  195.       По карте видно что каждое следующее чтение затрагивает кусочек предыдущего буфера
  196.       его то и кэшируем
  197.  
  198.   }
  199.   TsuCacheProxyStream = class(TsuReadOnlyProxyStream)
  200.   private
  201.     const
  202.       CACHE_COUNT_ITEMS = 100; //Количество кэшируемых операций чтения
  203.     type
  204.       //Элемент кэша одной операции чтения
  205.       TCacheItem = class
  206.       strict private
  207.         FCache: PByte;         //Указатель на буфер с кэшом
  208.         FCacheEnd: Int64;      //До какой позиции в стриме данные закешированны
  209.         FCacheStart: Int64;    //Позиция закешированного блока в стриме
  210.         FLastAccess: LongWord; //Время последнего обращения к кэшу
  211.  
  212.         function GetCache: PByte;
  213.       public
  214.         constructor Create(ABuffer: PByte);
  215.  
  216.         property Cache: PByte read GetCache;
  217.         property LastAccess: LongWord read FLastAccess;
  218.         property CacheEnd: Int64 read FCacheEnd write FCacheEnd;
  219.         property CacheStart: Int64 read FCacheStart write FCacheStart;
  220.       end;
  221.   private
  222.     FFullCache: PByte; //Непрерывный блок содержащий в себе весь кэш
  223.     FCacheItemSize: LongWord;  //Размер блока кэша, считается по первому запросу на чтение из стрима
  224.     FCacheItems: TObjectlist<TCacheItem>; //Список кэшей
  225.  
  226.     procedure InitializeCache;
  227.   public
  228.     destructor Destroy; override;
  229.  
  230.     function Read(var Buffer; Count: Longint): Longint; override;
  231.   end;
  232.  
  233.   //Класс формирующий карту чтения стрима
  234.   //Карта чтения - это файл со строками формата Позиция,ЗапрошеноНаЧтение
  235.   TsuReadMapGeneratorProxyStream = class(TsuReadOnlyProxyStream)
  236.   private
  237.     FMapFile: TextFile;
  238.   public
  239.     constructor Create(AStream: TStream; const AMapFile: string; Ownership: TStreamOwnership = soOwned);
  240.     destructor Destroy; override;
  241.  
  242.     function Read(var Buffer; Count: Integer): Integer; override;
  243.   end;
  244.  
  245. implementation
  246.  
  247. { TsuStreamAdapter }
  248.  
  249. //Исрпавление ошибки с вызовом Integr версии Seek вместо Int64
  250. function TsuStreamAdapter.Seek(dlibMove: Int64; dwOrigin: Integer;
  251.   out libNewPosition: Int64): HRESULT;
  252. var
  253.   NewPos: LargeInt;
  254. begin
  255.   try
  256.     if (dwOrigin < STREAM_SEEK_SET) or (dwOrigin > STREAM_SEEK_END) then
  257.       Exit(STG_E_INVALIDFUNCTION);
  258.  
  259.     //Для того что бы вызвался Int64 вариант метода Seek,
  260.     //приводим dwOrigin к TSeekOrigin
  261.     NewPos := Stream.Seek(dlibMove, TSeekOrigin(dwOrigin));
  262.     if @libNewPosition <> nil then libNewPosition := NewPos;
  263.     Result := S_OK;
  264.   except
  265.     Result := STG_E_INVALIDPOINTER;
  266.   end;
  267. end;
  268.  
  269. { TsuOleStream }
  270.  
  271. //Добавляем поддержку больших стримов
  272. function TsuOleStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
  273. var
  274.   Pos: Largeint;
  275. begin
  276.   OleCheck(GetIStream.Seek(Offset, Integer(Origin), Pos));
  277.   Result := Pos;
  278. end;
  279.  
  280. { TsuInMemoryStream }
  281.  
  282. //Поток получает ссылку на инициализированный участок памяти и работает с ним как обычный
  283. //MemoryStream
  284. constructor TsuInMemoryStream.Create(Memory: Pointer; const Size: Int64; AOwned: Boolean);
  285. begin
  286.   FOwned := AOwned;
  287.   SetPointer(Memory, Size);
  288. end;
  289.  
  290. destructor TsuInMemoryStream.Destroy;
  291. begin
  292.   if FOwned then
  293.     FreeMem(Memory, Size);
  294.  
  295.   inherited;
  296. end;
  297.  
  298. function TsuInMemoryStream.Write(const Buffer; Count: Integer): Longint;
  299. begin
  300.   raise EStreamError.CreateRes(@SWriteError);
  301. end;
  302.  
  303. { TsuPartStream }
  304.  
  305. //Стрим для работы с куском стрима
  306. //@param(AStream стрим)
  307. //@param(APartOffset смещение в стриме)
  308. //@param(APartSize размер куска после смещения)
  309. constructor TsuPartStream.Create(AStream: TStream; const APartOffset, APartSize: Int64; IsOwner: Boolean);
  310. begin
  311.   inherited Create;
  312.  
  313.   FPosition := 0;
  314.   FStream := AStream;
  315.   FIsOwner := IsOwner;
  316.   FPartSize := APartSize;
  317.   FPartOffset := APartOffset;
  318. end;
  319.  
  320. function TsuPartStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
  321. var
  322.   Temp: Int64;
  323. begin
  324.   Temp := FPosition;
  325.  
  326.   case Ord(Origin) of
  327.     soFromBeginning: Temp := Offset;
  328.     soFromCurrent: Temp := FPosition + Offset;
  329.     soFromEnd: Temp := FPartSize - Offset;
  330.   end;
  331.  
  332.   if (Temp < 0) then
  333.     Temp := 0;
  334.  
  335.   if (Temp > FPartSize) then
  336.     Temp := FPartSize;
  337.  
  338.   FPosition := Temp;
  339.   Result := FPosition;
  340. end;
  341.  
  342. //Уничтожение стрима
  343. destructor TsuPartStream.Destroy;
  344. begin
  345.   if FIsOwner then
  346.     FreeAndNil(FStream);
  347.  
  348.   inherited;
  349. end;
  350.  
  351. function TsuPartStream.Read(var Buffer; Count: Integer): Longint;
  352. var
  353.   MaxReadSize: Int64;
  354. begin
  355.   FStream.Position := FPartOffset + FPosition;
  356.  
  357.   //Вычисляем объем который мы можем прочитать из файл с текущей позиции
  358.   MaxReadSize := Size - Position;
  359.  
  360.   Result := FStream.Read(Buffer, Min(Count, MaxReadSize));
  361.  
  362.   FPosition := FStream.Position - FPartOffset;
  363. end;
  364.  
  365. function TsuPartStream.Write(const Buffer; Count: Integer): Longint;
  366. var
  367.   MaxWriteSize: Int64;
  368. begin
  369.   FStream.Position := FPartOffset + FPosition;
  370.  
  371.   //Вычисляем объем который мы можем записать в файл с текущей позиции
  372.   MaxWriteSize := Size - Position;
  373.  
  374.   Result := FStream.Write(Buffer, Min(Count, MaxWriteSize));
  375.  
  376.   FPosition := FStream.Position - FPartOffset;
  377. end;
  378.  
  379. { TsuPartIStream }
  380.  
  381. constructor TsuPartIStream.Create(AStream: IStream; const APartOffset,
  382.   APartSize: Int64);
  383. begin
  384.   inherited Create;
  385.  
  386.   FStream := AStream;
  387.   FPartSize := APartSize;
  388.   FPartOffset := APartOffset;
  389. end;
  390.  
  391. function TsuPartIStream.Clone(out stm: IStream): HResult;
  392. var
  393.   ClonedSource: IStream;
  394. begin
  395.   Result := S_FALSE;
  396.  
  397.   if Succeeded(FStream.Clone(ClonedSource)) then
  398.   begin
  399.     stm := TsuPartIStream.Create(ClonedSource, FPartOffset, FPartSize);
  400.     Result := S_OK;
  401.   end;
  402. end;
  403.  
  404. function TsuPartIStream.Commit(grfCommitFlags: Integer): HResult;
  405. begin
  406.   Result := E_NOTIMPL;
  407. end;
  408.  
  409. function TsuPartIStream.CopyTo(stm: IStream; cb: Largeint; out cbRead,
  410.   cbWritten: Largeint): HResult;
  411. begin
  412.   Result := E_NOTIMPL;
  413. end;
  414.  
  415. function TsuPartIStream.LockRegion(libOffset, cb: Largeint;
  416.   dwLockType: Integer): HResult;
  417. begin
  418.   Result := E_NOTIMPL;
  419. end;
  420.  
  421. function TsuPartIStream.Read(pv: Pointer; cb: Integer;
  422.   pcbRead: PLongint): HResult;
  423. var
  424.   Temp: Int64;
  425.   Size: Int64;
  426.   Position: Int64;
  427.   MaxReadSize: Int64;
  428. begin
  429.   try
  430.     if pv = nil then
  431.       Exit(STG_E_INVALIDPOINTER);
  432.  
  433.     OleCheck(FStream.Seek(FPartOffset + FPosition, STREAM_SEEK_SET, Temp));
  434.  
  435.     //Вычисляем объем который мы можем прочитать из файл с текущей позиции
  436.  
  437.     OleCheck(Seek(0, STREAM_SEEK_CUR, Position));
  438.  
  439.     OleCheck(Seek(0, STREAM_SEEK_END, Size));
  440.  
  441.     //Восстановим позицию
  442.     OleCheck(Seek(Position, STREAM_SEEK_SET, Position));
  443.  
  444.     MaxReadSize := Size - Position;
  445.  
  446.     FStream.Read(pv, Min(cb, MaxReadSize), pcbRead);
  447.  
  448.     OleCheck(FStream.Seek(0, STREAM_SEEK_CUR, Position));
  449.  
  450.     FPosition := Position - FPartOffset;
  451.  
  452.     Result := S_OK;
  453.   except
  454.     Result := S_FALSE;
  455.   end;
  456. end;
  457.  
  458. function TsuPartIStream.Revert: HResult;
  459. begin
  460.   Result := E_NOTIMPL;
  461. end;
  462.  
  463. function TsuPartIStream.Seek(dlibMove: Largeint; dwOrigin: Integer;
  464.   out libNewPosition: Largeint): HResult;
  465. var
  466.   Temp: Int64;
  467. begin
  468.   case dwOrigin of
  469.     STREAM_SEEK_SET: Temp := dlibMove;
  470.     STREAM_SEEK_CUR: Temp := FPosition + dlibMove;
  471.     STREAM_SEEK_END: Temp := FPartSize - dlibMove;
  472.     else
  473.       Exit(STG_E_INVALIDFUNCTION);
  474.   end;
  475.  
  476.   if (Temp < 0) or (Temp > FPartSize) then
  477.     Exit(STG_E_SEEKERROR);
  478.  
  479.   FPosition := Temp;
  480.   libNewPosition := FPosition;
  481.  
  482.   Result := S_OK;
  483. end;
  484.  
  485. function TsuPartIStream.SetSize(libNewSize: Largeint): HResult;
  486. begin
  487.   Result := E_NOTIMPL;
  488. end;
  489.  
  490. function TsuPartIStream.Stat(out statstg: TStatStg;
  491.   grfStatFlag: Integer): HResult;
  492. begin
  493.   Result := E_NOTIMPL;
  494. end;
  495.  
  496. function TsuPartIStream.UnlockRegion(libOffset, cb: Largeint;
  497.   dwLockType: Integer): HResult;
  498. begin
  499.   Result := E_NOTIMPL;
  500. end;
  501.  
  502. function TsuPartIStream.Write(pv: Pointer; cb: Integer;
  503.   pcbWritten: PLongint): HResult;
  504. begin
  505.   Result := E_NOTIMPL;
  506. end;
  507.  
  508. { TsuIFileStream }
  509.  
  510. function TsuIFileStream.Clone(out stm: IStream): HRESULT;
  511. begin
  512.   with Stream as TFileStream do
  513.     stm := TsuIFileStream.Create(FileName, FMode);
  514.  
  515.   Result := S_OK;
  516. end;
  517.  
  518. constructor TsuIFileStream.Create(const AFileName: string; Mode: Word);
  519. begin
  520.   inherited Create(TFileStream.Create(AFileName, Mode), soOwned);
  521.  
  522.   FMode := Mode;
  523. end;
  524.  
  525. { TsuIFileMemoryStream }
  526.  
  527. function TsuIFileMemoryStream.Clone(out stm: IStream): HRESULT;
  528. begin
  529.   stm := TsuIFileMemoryStream.Create(FFileName);
  530.  
  531.   Result := S_OK;
  532. end;
  533.  
  534. constructor TsuIFileMemoryStream.Create(const AFileName: string);
  535. begin
  536.   FFileName := AFileName;
  537.  
  538.   inherited Create(TMemoryStream.Create, soOwned);
  539.  
  540.   with Stream as TMemoryStream do
  541.     LoadFromFile(AFileName);
  542. end;
  543.  
  544. { TsuResourceStreamAdapter }
  545.  
  546. constructor TsuResourceStreamAdapter.Create(Stream: TResourceStream;
  547.   Ownership: TStreamOwnership; AInstance: THandle; const AResName: string);
  548. begin
  549.   inherited Create(Stream, Ownership);
  550.  
  551.   FResName := AResName;
  552.   FInstance := AInstance;
  553. end;
  554.  
  555. function TsuResourceStreamAdapter.Clone(out stm: IStream): HResult;
  556. var
  557.   Stream: TResourceStream;
  558. begin
  559.   Stream := TResourceStream.Create(FInstance, FResName, RT_RCDATA);
  560.   stm := TsuResourceStreamAdapter.Create(Stream, soOwned, FInstance, FResName) as IStream;
  561.  
  562.   Result := S_OK;
  563. end;
  564.  
  565. { TsuProxyStream }
  566.  
  567. constructor TsuProxyStream.Create(AStream: TStream; Ownership: TStreamOwnership);
  568. begin
  569.   FStream := AStream;
  570.   FOwnership := Ownership;
  571. end;
  572.  
  573. destructor TsuProxyStream.Destroy;
  574. begin
  575.   //Освобождаем стрим если мы стали его владельцем
  576.   if FOwnership = soOwned then
  577.     FreeAndNil(FStream);
  578.  
  579.   inherited;
  580. end;
  581.  
  582. function TsuProxyStream.Read(var Buffer; Count: Integer): Longint;
  583. begin
  584.   Result := FStream.Read(Buffer, Count);
  585. end;
  586.  
  587. function TsuProxyStream.Seek(const Offset: Int64;
  588.   Origin: TSeekOrigin): Int64;
  589. begin
  590.   Result := FStream.Seek(Offset, Origin);
  591. end;
  592.  
  593. function TsuProxyStream.Write(const Buffer; Count: Integer): Longint;
  594. begin
  595.   Result := FStream.Write(Buffer, Count);
  596. end;
  597.  
  598. { TsuReadOnlyProxyStream }
  599.  
  600. function TsuReadOnlyProxyStream.Write(const Buffer; Count: Integer): Longint;
  601. begin
  602.   raise EStreamError.CreateRes(@SWriteError);
  603. end;
  604.  
  605. { TsuCacheProxyStream.TCacheItem }
  606.  
  607. constructor TsuCacheProxyStream.TCacheItem.Create(ABuffer: PByte);
  608. begin
  609.   FCache := ABuffer
  610. end;
  611.  
  612. //Получение указателя на буфер для кэша
  613. function TsuCacheProxyStream.TCacheItem.GetCache: PByte;
  614. begin
  615.   Result := FCache;
  616.  
  617.   //Запоминаем время последнего обращения к кэшу
  618.   FLastAccess := Ticks;
  619. end;
  620.  
  621. { TsuCacheProxyStream }
  622.  
  623. destructor TsuCacheProxyStream.Destroy;
  624. begin
  625.   FreeAndNil(FCacheItems);
  626.  
  627.   //Освобождаем кэш
  628.   FreeMem(FFullCache);
  629.  
  630.   inherited;
  631. end;
  632.  
  633. //Инициализация кэша
  634. procedure TsuCacheProxyStream.InitializeCache;
  635. var
  636.   Loop: LongWord;
  637. begin
  638.   Assert(FCacheItemSize <> 0);
  639.  
  640.   //Выделяем память под кэш
  641.   GetMem(FFullCache, FCacheItemSize * CACHE_COUNT_ITEMS);
  642.  
  643.   FCacheItems := TObjectList<TCacheItem>.Create(True);
  644.   FCacheItems.Capacity := CACHE_COUNT_ITEMS;
  645.  
  646.   for Loop := 0 to CACHE_COUNT_ITEMS - 1 do
  647.     //Применяем указатель на нужную область в кэше
  648.     FCacheItems.Add(TCacheItem.Create(FFullCache + Loop * FCacheItemSize));
  649. end;
  650.  
  651. function TsuCacheProxyStream.Read(var Buffer; Count: Integer): Longint;
  652. var
  653.   Loop: Integer;
  654.   Offset: LongWord;
  655.   ReadPosition: Int64;
  656.   OldestIndex: Integer;
  657.   EndCacheIndex: Integer;
  658.   ReadPositionEnd: Int64;
  659.   ReadedFromCache: Integer;
  660.   ReadedFromStream: Integer;
  661.   BeforeReadCacheIndex: Integer;
  662. begin
  663.   if Count = 0 then
  664.     Exit(0);
  665.  
  666.   //Запомним позицию с которой считывают
  667.   ReadPosition := Position;
  668.   ReadPositionEnd := ReadPosition + Count;
  669.  
  670.   //Если размер элемента для кэша еще не определен
  671.   if FCacheItemSize = 0 then
  672.   begin
  673.     Result := FStream.Read(Buffer, Count);
  674.  
  675.     FCacheItemSize := Count;
  676.  
  677.     //Посчитаем 100 элементов
  678.     InitializeCache;
  679.  
  680.     //Раз уж данные прочитали, занесем их в кэш
  681.     FCacheItems[0].CacheStart := ReadPosition;
  682.     FCacheItems[0].CacheEnd := ReadPosition + Min(Result, FCacheItemSize);
  683.     Move(Buffer, FCacheItems[0].Cache^, Min(Result, FCacheItemSize));
  684.     Exit;
  685.   end;
  686.  
  687.   //Поищем, есть ли в кэше какой-либо элемент
  688.   OldestIndex := 0;
  689.  
  690.   //Будем искать кэш в котором есть данные для конца запрашиваемого буфера
  691.   EndCacheIndex := -1;
  692.  
  693.   //Будем искать кэш в котором есть еще свободное место и который заканчивается
  694.   //на те данные что мы будем читать
  695.   BeforeReadCacheIndex := -1;
  696.  
  697.   for Loop := 0 to FCacheItems.Count - 1 do
  698.     with FCacheItems[Loop] do
  699.     begin
  700.       if LastAccess < FCacheItems[OldestIndex].LastAccess then
  701.         OldestIndex := Loop;
  702.  
  703.       if ((CacheEnd - CacheStart) < FCacheItemSize) and (ReadPosition = CacheEnd + 1) then
  704.         BeforeReadCacheIndex := Loop;
  705.  
  706.       if (ReadPositionEnd > CacheStart) and (ReadPositionEnd <= CacheEnd) then
  707.         //Запомим индекскэша если он нашелся впервые, либо, если в найденном буфере
  708.         //больше закэшированно данных чем в том что нашелся ранее
  709.         if (EndCacheIndex = -1) or (CacheStart < FCacheItems[EndCacheIndex].CacheStart) then
  710.           EndCacheIndex := Loop;
  711.  
  712.       //Проверим, есть ли в этом буфере какие-нибудь данные для нас
  713.       if (ReadPosition >= CacheStart) and (ReadPosition < CacheEnd) then
  714.       begin
  715.         //Расчитаем с какой позиции в кэше хранятся данные длятекущего рида
  716.         Offset := ReadPosition - CacheStart;
  717.  
  718.         //Расчитаем сколько данных из кэша можем взять
  719.         ReadedFromCache := Min(Count, (CacheEnd - CacheStart) - Offset);
  720.  
  721.         //Собственно возьмем данные из кэша
  722.         Move((Cache + Offset)^, Buffer, ReadedFromCache);
  723.  
  724.         //Прочтем оставшиеся данные из стрима, те которых не было в кэше
  725.         Seek(ReadedFromCache, soCurrent);
  726.  
  727.         //Рекурсивно прочитаем остаток, что бы он либо из другого кэша взялся,
  728.         //либо закэшировался дальше
  729.         ReadedFromStream := Read(PByte(PByte(@Buffer) + ReadedFromCache)^, Count - ReadedFromCache);
  730.  
  731.         //Расчитам общее количество прочитанных байт
  732.         Result := ReadedFromCache + ReadedFromStream;
  733.  
  734.         //Восстановим позицию, потому что в рекурсии она могла меняться
  735.         FStream.Position := ReadPosition + Result;
  736.  
  737.         Exit;
  738.       end;
  739.     end;
  740.  
  741.   //Может быть с конца буфера что-то есть
  742.   if EndCacheIndex <> -1 then
  743.     with FCacheItems[EndCacheIndex] do
  744.     begin
  745.       //Расчитаем сколько данных из кэша можем взять
  746.       ReadedFromCache := ReadPositionEnd - CacheStart;
  747.  
  748.       Offset := Count - ReadedFromCache;
  749.  
  750.       //Собственно возьмем данные из кэша
  751.       Move(Cache^, (PByte(@Buffer) + Offset)^, ReadedFromCache);
  752.  
  753.       //А теперь прочитаем из стрима
  754.       ReadedFromStream := Read(Buffer, Offset);
  755.  
  756.       //Расчитам общее количество прочитанных байт
  757.       Result := ReadedFromStream + ReadedFromCache;
  758.  
  759.       FStream.Position := ReadPosition + Result;
  760.  
  761.       Exit;
  762.     end;
  763.  
  764.   //Если данных в буфере не нашли, прочитаем их из стрима и закэшируем
  765.  
  766.   //Если уже есть кэш, в котором есть свободное место, и данные хранящиеся в нем
  767.   //являются предварительными  заканчиваются прямо перед
  768.   if BeforeReadCacheIndex <> -1 then
  769.     with FCacheItems[BeforeReadCacheIndex] do
  770.     begin
  771.       Result := FStream.Read(Buffer, Min(Count, FCacheItemSize - (CacheEnd - CacheStart)));
  772.       if Result > 0 then
  773.       begin
  774.         //Закэшируем в новый буфер
  775.         Move(Buffer, (Cache + (CacheEnd - CacheStart))^, Result);
  776.  
  777.         CacheEnd := CacheEnd + Result;
  778.  
  779.         Result := Result + Read(PByte(PByte(@Buffer) + Result)^, Count - Result);
  780.       end;
  781.  
  782.       Exit;
  783.     end;
  784.  
  785.   //Проситаем данные и перезапишим ими самый давно неиспользуемый кэш
  786.   Result := FStream.Read(Buffer, Min(Count, FCacheItemSize));
  787.   if Result > 0 then
  788.   begin
  789.     //Закэшируем в новый буфер
  790.     FCacheItems[OldestIndex].CacheStart := ReadPosition;
  791.     FCacheItems[OldestIndex].CacheEnd := ReadPosition + Result;
  792.     Move(Buffer, FCacheItems[OldestIndex].Cache^, Result);
  793.  
  794.     //Если мы прочитали только размер буфера, дочитаем остаток
  795.     if (Result = Integer(FCacheItemSize)) and (Result < Count) then
  796.       Result := Result + Read(PByte(PByte(@Buffer) + Result)^, Count - Result);
  797.   end;
  798. end;
  799.  
  800. { TsuReadMapGeneratorProxyStream }
  801.  
  802. constructor TsuReadMapGeneratorProxyStream.Create(AStream: TStream;
  803.   const AMapFile: string; Ownership: TStreamOwnership);
  804. begin
  805.   Assign(FMapFile, AMapFile);
  806.   Rewrite(FMapFile);
  807.  
  808.   inherited Create(AStream, Ownership);
  809. end;
  810.  
  811. destructor TsuReadMapGeneratorProxyStream.Destroy;
  812. begin
  813.   Flush(FMapFile);
  814.   Close(FMapFile);
  815.  
  816.   inherited;
  817. end;
  818.  
  819. function TsuReadMapGeneratorProxyStream.Read(var Buffer;
  820.   Count: Integer): Integer;
  821. begin
  822.   Writeln(FMapFile, Format('%u,%u',[Position, Count]));
  823.  
  824.   Result := inherited;
  825. end;
  826.  
  827. end.
Add Comment
Please, Sign In to add comment