Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- {
- Модуль содержит различные классы, для работы с потоками.
- }
- unit suStreamsUnit;
- {$WEAKLINKRTTI ON}
- {$RTTI EXPLICIT METHODS([]) PROPERTIES([]) FIELDS([])}
- interface
- {$I CompilerDefines.inc}
- uses
- {$IFDEF VCL_XE2_OR_ABOVE}
- {$IFDEF MSWINDOWS}
- Winapi.Windows, Winapi.ActiveX, System.Win.ComObj, Vcl.AxCtrls,
- {$ENDIF}
- {$IFDEF POSIX}
- System.Types, suComHelpersUnit.Posix,
- {$ENDIF POSIX}
- System.Classes, System.SysUtils, System.Math, System.RTLConsts,
- {$ELSE}
- Windows, Classes, SysUtils, ActiveX, AxCtrls, ComObj, Math, RTLConsts,
- {$ENDIF}
- Generics.Collections,
- suMiscCrossPlatformUtilsUnit;
- type
- //Поток получает ссылку на инициализированный участок памяти и работает с ним как обычный
- //MemoryStream
- TsuInMemoryStream = class(TCustomMemoryStream)
- private
- FOwned: Boolean;
- public
- constructor Create(Memory: Pointer; const Size: Int64; AOwned: Boolean = False);
- destructor Destroy; override;
- function Write(const Buffer; Count: Longint): Longint; override;
- end;
- //Исправленный TStreamAdapter
- TsuStreamAdapter = class(TStreamAdapter)
- public
- function Seek(dlibMove: Int64; dwOrigin: Integer;
- out libNewPosition: Int64): HRESULT; override; stdcall;
- end;
- //Исправленный TOleStream
- TsuOleStream = class(TOleStream)
- public
- function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;
- property OwnerStream: IStream read GetIStream;
- end;
- //Класс, работает с частью стрима как со стримом.
- TsuPartStream = class(TStream)
- private
- FStream: TStream;
- FPartSize: Int64;
- FPosition: Int64;
- FIsOwner: Boolean;
- FPartOffset: Int64;
- public
- //Стрим для работы с куском стрима
- //@param(AStream стрим)
- //@param(APartOffset смещение в стриме)
- //@param(APartSize размер куска после смещения)
- //@param(IsOwner самостоятельно уничтожить стрим
- constructor Create(AStream: TStream; const APartOffset, APartSize: Int64; IsOwner: Boolean = False);
- destructor Destroy; override;
- function Read(var Buffer; Count: Longint): Longint; override;
- function Write(const Buffer; Count: Integer): Longint; override;
- function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;
- //Смещение в стриме
- property PartOffset: Int64 read FPartOffset;
- end;
- //Класс, работает с частью истрима как со истримом.
- TsuPartIStream = class(TInterfacedObject, IStream)
- private
- FStream: IStream;
- FPartSize: Int64;
- FPosition: Int64;
- FPartOffset: Int64;
- public
- //Стрим для работы с куском стрима
- //@param(AStream стрим)
- //@param(APartOffset смещение в стриме)
- //@param(APartSize размер куска после смещения)
- constructor Create(AStream: IStream; const APartOffset, APartSize: Int64);
- function Read(pv: Pointer; cb: Longint; pcbRead: PLongint): HResult; stdcall;
- function Write(pv: Pointer; cb: Longint; pcbWritten: PLongint): HResult; stdcall;
- function Seek(dlibMove: Largeint; dwOrigin: Longint; out libNewPosition: Largeint): HResult; stdcall;
- function SetSize(libNewSize: Largeint): HResult; stdcall;
- function CopyTo(stm: IStream; cb: Largeint; out cbRead: Largeint;
- out cbWritten: Largeint): HResult; stdcall;
- function Commit(grfCommitFlags: Longint): HResult; stdcall;
- function Revert: HResult; stdcall;
- function LockRegion(libOffset: Largeint; cb: Largeint;
- dwLockType: Longint): HResult; stdcall;
- function UnlockRegion(libOffset: Largeint; cb: Largeint;
- dwLockType: Longint): HResult; stdcall;
- function Stat(out statstg: TStatStg;
- grfStatFlag: Longint): HResult; stdcall;
- function Clone(out stm: IStream): HResult; stdcall;
- //Смещение в стриме
- property PartOffset: Int64 read FPartOffset;
- end;
- TsuIFileStream = class(TsuStreamAdapter)
- private
- FMode: Word;
- public
- constructor Create(const AFileName: string; Mode: Word);
- function Clone(out stm: IStream): HRESULT; override; stdcall;
- end;
- TsuIFileMemoryStream = class(TsuStreamAdapter)
- private
- FFileName: string;
- public
- constructor Create(const AFileName: string);
- function Clone(out stm: IStream): HRESULT; override; stdcall;
- end;
- TsuResourceStreamAdapter = class(TsuStreamAdapter)
- private
- FResName: string;
- FInstance: THandle;
- public
- constructor Create(Stream: TResourceStream; Ownership: TStreamOwnership; AInstance: THandle; const AResName: string);
- function Clone(out stm: IStream): HResult; override; stdcall;
- end;
- //Базовый класс для создания прокси к стримам
- TsuProxyStream = class(TStream)
- private
- FStream: TStream;
- FOwnership: TStreamOwnership;
- public
- constructor Create(AStream: TStream; Ownership: TStreamOwnership = soOwned);
- destructor Destroy; override;
- function Read(var Buffer; Count: Longint): Longint; override;
- function Write(const Buffer; Count: Longint): Longint; override;
- function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;
- property Stream: TStream read FStream;
- end;
- TsuReadOnlyProxyStream = class(TsuProxyStream)
- public
- function Write(const Buffer; Count: Longint): Longint; override;
- end;
- {
- Стрим кэшируючий чтение раннее прочитанных блоков. Сделан для оптимизации
- чтения mp4 файлов флэшом.
- Было проанализировано чтение стрима флэшом, и выяснилось что в за время
- просмотра ролика, чтение данных из файла превосходит размер самого файла
- от 2х до 6ти.
- Соответсвенно была построена карта доступа к файлу, и после проведения тестов,
- оказалось, что стрим читается до 6ти раз дольше чем если бы было прямое
- чтение.
- Благодаря данному классу удалось ускорить чтение по карте доступа от 30 до 50%.
- Для наглядности пример карта доступа:
- Позиция, Кол-во прочитанных данных
- 24515,16384
- 9338614,16384
- 9339160,16384
- 24523,16384
- 9347688,16384
- 37111,16384
- 25192,16384
- 41576,16384
- 37394,16384
- 53778,16384
- 37421,16384
- 53809,16384
- По карте видно что каждое следующее чтение затрагивает кусочек предыдущего буфера
- его то и кэшируем
- }
- TsuCacheProxyStream = class(TsuReadOnlyProxyStream)
- private
- const
- CACHE_COUNT_ITEMS = 100; //Количество кэшируемых операций чтения
- type
- //Элемент кэша одной операции чтения
- TCacheItem = class
- strict private
- FCache: PByte; //Указатель на буфер с кэшом
- FCacheEnd: Int64; //До какой позиции в стриме данные закешированны
- FCacheStart: Int64; //Позиция закешированного блока в стриме
- FLastAccess: LongWord; //Время последнего обращения к кэшу
- function GetCache: PByte;
- public
- constructor Create(ABuffer: PByte);
- property Cache: PByte read GetCache;
- property LastAccess: LongWord read FLastAccess;
- property CacheEnd: Int64 read FCacheEnd write FCacheEnd;
- property CacheStart: Int64 read FCacheStart write FCacheStart;
- end;
- private
- FFullCache: PByte; //Непрерывный блок содержащий в себе весь кэш
- FCacheItemSize: LongWord; //Размер блока кэша, считается по первому запросу на чтение из стрима
- FCacheItems: TObjectlist<TCacheItem>; //Список кэшей
- procedure InitializeCache;
- public
- destructor Destroy; override;
- function Read(var Buffer; Count: Longint): Longint; override;
- end;
- //Класс формирующий карту чтения стрима
- //Карта чтения - это файл со строками формата Позиция,ЗапрошеноНаЧтение
- TsuReadMapGeneratorProxyStream = class(TsuReadOnlyProxyStream)
- private
- FMapFile: TextFile;
- public
- constructor Create(AStream: TStream; const AMapFile: string; Ownership: TStreamOwnership = soOwned);
- destructor Destroy; override;
- function Read(var Buffer; Count: Integer): Integer; override;
- end;
- implementation
- { TsuStreamAdapter }
- //Исрпавление ошибки с вызовом Integr версии Seek вместо Int64
- function TsuStreamAdapter.Seek(dlibMove: Int64; dwOrigin: Integer;
- out libNewPosition: Int64): HRESULT;
- var
- NewPos: LargeInt;
- begin
- try
- if (dwOrigin < STREAM_SEEK_SET) or (dwOrigin > STREAM_SEEK_END) then
- Exit(STG_E_INVALIDFUNCTION);
- //Для того что бы вызвался Int64 вариант метода Seek,
- //приводим dwOrigin к TSeekOrigin
- NewPos := Stream.Seek(dlibMove, TSeekOrigin(dwOrigin));
- if @libNewPosition <> nil then libNewPosition := NewPos;
- Result := S_OK;
- except
- Result := STG_E_INVALIDPOINTER;
- end;
- end;
- { TsuOleStream }
- //Добавляем поддержку больших стримов
- function TsuOleStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
- var
- Pos: Largeint;
- begin
- OleCheck(GetIStream.Seek(Offset, Integer(Origin), Pos));
- Result := Pos;
- end;
- { TsuInMemoryStream }
- //Поток получает ссылку на инициализированный участок памяти и работает с ним как обычный
- //MemoryStream
- constructor TsuInMemoryStream.Create(Memory: Pointer; const Size: Int64; AOwned: Boolean);
- begin
- FOwned := AOwned;
- SetPointer(Memory, Size);
- end;
- destructor TsuInMemoryStream.Destroy;
- begin
- if FOwned then
- FreeMem(Memory, Size);
- inherited;
- end;
- function TsuInMemoryStream.Write(const Buffer; Count: Integer): Longint;
- begin
- raise EStreamError.CreateRes(@SWriteError);
- end;
- { TsuPartStream }
- //Стрим для работы с куском стрима
- //@param(AStream стрим)
- //@param(APartOffset смещение в стриме)
- //@param(APartSize размер куска после смещения)
- constructor TsuPartStream.Create(AStream: TStream; const APartOffset, APartSize: Int64; IsOwner: Boolean);
- begin
- inherited Create;
- FPosition := 0;
- FStream := AStream;
- FIsOwner := IsOwner;
- FPartSize := APartSize;
- FPartOffset := APartOffset;
- end;
- function TsuPartStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
- var
- Temp: Int64;
- begin
- Temp := FPosition;
- case Ord(Origin) of
- soFromBeginning: Temp := Offset;
- soFromCurrent: Temp := FPosition + Offset;
- soFromEnd: Temp := FPartSize - Offset;
- end;
- if (Temp < 0) then
- Temp := 0;
- if (Temp > FPartSize) then
- Temp := FPartSize;
- FPosition := Temp;
- Result := FPosition;
- end;
- //Уничтожение стрима
- destructor TsuPartStream.Destroy;
- begin
- if FIsOwner then
- FreeAndNil(FStream);
- inherited;
- end;
- function TsuPartStream.Read(var Buffer; Count: Integer): Longint;
- var
- MaxReadSize: Int64;
- begin
- FStream.Position := FPartOffset + FPosition;
- //Вычисляем объем который мы можем прочитать из файл с текущей позиции
- MaxReadSize := Size - Position;
- Result := FStream.Read(Buffer, Min(Count, MaxReadSize));
- FPosition := FStream.Position - FPartOffset;
- end;
- function TsuPartStream.Write(const Buffer; Count: Integer): Longint;
- var
- MaxWriteSize: Int64;
- begin
- FStream.Position := FPartOffset + FPosition;
- //Вычисляем объем который мы можем записать в файл с текущей позиции
- MaxWriteSize := Size - Position;
- Result := FStream.Write(Buffer, Min(Count, MaxWriteSize));
- FPosition := FStream.Position - FPartOffset;
- end;
- { TsuPartIStream }
- constructor TsuPartIStream.Create(AStream: IStream; const APartOffset,
- APartSize: Int64);
- begin
- inherited Create;
- FStream := AStream;
- FPartSize := APartSize;
- FPartOffset := APartOffset;
- end;
- function TsuPartIStream.Clone(out stm: IStream): HResult;
- var
- ClonedSource: IStream;
- begin
- Result := S_FALSE;
- if Succeeded(FStream.Clone(ClonedSource)) then
- begin
- stm := TsuPartIStream.Create(ClonedSource, FPartOffset, FPartSize);
- Result := S_OK;
- end;
- end;
- function TsuPartIStream.Commit(grfCommitFlags: Integer): HResult;
- begin
- Result := E_NOTIMPL;
- end;
- function TsuPartIStream.CopyTo(stm: IStream; cb: Largeint; out cbRead,
- cbWritten: Largeint): HResult;
- begin
- Result := E_NOTIMPL;
- end;
- function TsuPartIStream.LockRegion(libOffset, cb: Largeint;
- dwLockType: Integer): HResult;
- begin
- Result := E_NOTIMPL;
- end;
- function TsuPartIStream.Read(pv: Pointer; cb: Integer;
- pcbRead: PLongint): HResult;
- var
- Temp: Int64;
- Size: Int64;
- Position: Int64;
- MaxReadSize: Int64;
- begin
- try
- if pv = nil then
- Exit(STG_E_INVALIDPOINTER);
- OleCheck(FStream.Seek(FPartOffset + FPosition, STREAM_SEEK_SET, Temp));
- //Вычисляем объем который мы можем прочитать из файл с текущей позиции
- OleCheck(Seek(0, STREAM_SEEK_CUR, Position));
- OleCheck(Seek(0, STREAM_SEEK_END, Size));
- //Восстановим позицию
- OleCheck(Seek(Position, STREAM_SEEK_SET, Position));
- MaxReadSize := Size - Position;
- FStream.Read(pv, Min(cb, MaxReadSize), pcbRead);
- OleCheck(FStream.Seek(0, STREAM_SEEK_CUR, Position));
- FPosition := Position - FPartOffset;
- Result := S_OK;
- except
- Result := S_FALSE;
- end;
- end;
- function TsuPartIStream.Revert: HResult;
- begin
- Result := E_NOTIMPL;
- end;
- function TsuPartIStream.Seek(dlibMove: Largeint; dwOrigin: Integer;
- out libNewPosition: Largeint): HResult;
- var
- Temp: Int64;
- begin
- case dwOrigin of
- STREAM_SEEK_SET: Temp := dlibMove;
- STREAM_SEEK_CUR: Temp := FPosition + dlibMove;
- STREAM_SEEK_END: Temp := FPartSize - dlibMove;
- else
- Exit(STG_E_INVALIDFUNCTION);
- end;
- if (Temp < 0) or (Temp > FPartSize) then
- Exit(STG_E_SEEKERROR);
- FPosition := Temp;
- libNewPosition := FPosition;
- Result := S_OK;
- end;
- function TsuPartIStream.SetSize(libNewSize: Largeint): HResult;
- begin
- Result := E_NOTIMPL;
- end;
- function TsuPartIStream.Stat(out statstg: TStatStg;
- grfStatFlag: Integer): HResult;
- begin
- Result := E_NOTIMPL;
- end;
- function TsuPartIStream.UnlockRegion(libOffset, cb: Largeint;
- dwLockType: Integer): HResult;
- begin
- Result := E_NOTIMPL;
- end;
- function TsuPartIStream.Write(pv: Pointer; cb: Integer;
- pcbWritten: PLongint): HResult;
- begin
- Result := E_NOTIMPL;
- end;
- { TsuIFileStream }
- function TsuIFileStream.Clone(out stm: IStream): HRESULT;
- begin
- with Stream as TFileStream do
- stm := TsuIFileStream.Create(FileName, FMode);
- Result := S_OK;
- end;
- constructor TsuIFileStream.Create(const AFileName: string; Mode: Word);
- begin
- inherited Create(TFileStream.Create(AFileName, Mode), soOwned);
- FMode := Mode;
- end;
- { TsuIFileMemoryStream }
- function TsuIFileMemoryStream.Clone(out stm: IStream): HRESULT;
- begin
- stm := TsuIFileMemoryStream.Create(FFileName);
- Result := S_OK;
- end;
- constructor TsuIFileMemoryStream.Create(const AFileName: string);
- begin
- FFileName := AFileName;
- inherited Create(TMemoryStream.Create, soOwned);
- with Stream as TMemoryStream do
- LoadFromFile(AFileName);
- end;
- { TsuResourceStreamAdapter }
- constructor TsuResourceStreamAdapter.Create(Stream: TResourceStream;
- Ownership: TStreamOwnership; AInstance: THandle; const AResName: string);
- begin
- inherited Create(Stream, Ownership);
- FResName := AResName;
- FInstance := AInstance;
- end;
- function TsuResourceStreamAdapter.Clone(out stm: IStream): HResult;
- var
- Stream: TResourceStream;
- begin
- Stream := TResourceStream.Create(FInstance, FResName, RT_RCDATA);
- stm := TsuResourceStreamAdapter.Create(Stream, soOwned, FInstance, FResName) as IStream;
- Result := S_OK;
- end;
- { TsuProxyStream }
- constructor TsuProxyStream.Create(AStream: TStream; Ownership: TStreamOwnership);
- begin
- FStream := AStream;
- FOwnership := Ownership;
- end;
- destructor TsuProxyStream.Destroy;
- begin
- //Освобождаем стрим если мы стали его владельцем
- if FOwnership = soOwned then
- FreeAndNil(FStream);
- inherited;
- end;
- function TsuProxyStream.Read(var Buffer; Count: Integer): Longint;
- begin
- Result := FStream.Read(Buffer, Count);
- end;
- function TsuProxyStream.Seek(const Offset: Int64;
- Origin: TSeekOrigin): Int64;
- begin
- Result := FStream.Seek(Offset, Origin);
- end;
- function TsuProxyStream.Write(const Buffer; Count: Integer): Longint;
- begin
- Result := FStream.Write(Buffer, Count);
- end;
- { TsuReadOnlyProxyStream }
- function TsuReadOnlyProxyStream.Write(const Buffer; Count: Integer): Longint;
- begin
- raise EStreamError.CreateRes(@SWriteError);
- end;
- { TsuCacheProxyStream.TCacheItem }
- constructor TsuCacheProxyStream.TCacheItem.Create(ABuffer: PByte);
- begin
- FCache := ABuffer
- end;
- //Получение указателя на буфер для кэша
- function TsuCacheProxyStream.TCacheItem.GetCache: PByte;
- begin
- Result := FCache;
- //Запоминаем время последнего обращения к кэшу
- FLastAccess := Ticks;
- end;
- { TsuCacheProxyStream }
- destructor TsuCacheProxyStream.Destroy;
- begin
- FreeAndNil(FCacheItems);
- //Освобождаем кэш
- FreeMem(FFullCache);
- inherited;
- end;
- //Инициализация кэша
- procedure TsuCacheProxyStream.InitializeCache;
- var
- Loop: LongWord;
- begin
- Assert(FCacheItemSize <> 0);
- //Выделяем память под кэш
- GetMem(FFullCache, FCacheItemSize * CACHE_COUNT_ITEMS);
- FCacheItems := TObjectList<TCacheItem>.Create(True);
- FCacheItems.Capacity := CACHE_COUNT_ITEMS;
- for Loop := 0 to CACHE_COUNT_ITEMS - 1 do
- //Применяем указатель на нужную область в кэше
- FCacheItems.Add(TCacheItem.Create(FFullCache + Loop * FCacheItemSize));
- end;
- function TsuCacheProxyStream.Read(var Buffer; Count: Integer): Longint;
- var
- Loop: Integer;
- Offset: LongWord;
- ReadPosition: Int64;
- OldestIndex: Integer;
- EndCacheIndex: Integer;
- ReadPositionEnd: Int64;
- ReadedFromCache: Integer;
- ReadedFromStream: Integer;
- BeforeReadCacheIndex: Integer;
- begin
- if Count = 0 then
- Exit(0);
- //Запомним позицию с которой считывают
- ReadPosition := Position;
- ReadPositionEnd := ReadPosition + Count;
- //Если размер элемента для кэша еще не определен
- if FCacheItemSize = 0 then
- begin
- Result := FStream.Read(Buffer, Count);
- FCacheItemSize := Count;
- //Посчитаем 100 элементов
- InitializeCache;
- //Раз уж данные прочитали, занесем их в кэш
- FCacheItems[0].CacheStart := ReadPosition;
- FCacheItems[0].CacheEnd := ReadPosition + Min(Result, FCacheItemSize);
- Move(Buffer, FCacheItems[0].Cache^, Min(Result, FCacheItemSize));
- Exit;
- end;
- //Поищем, есть ли в кэше какой-либо элемент
- OldestIndex := 0;
- //Будем искать кэш в котором есть данные для конца запрашиваемого буфера
- EndCacheIndex := -1;
- //Будем искать кэш в котором есть еще свободное место и который заканчивается
- //на те данные что мы будем читать
- BeforeReadCacheIndex := -1;
- for Loop := 0 to FCacheItems.Count - 1 do
- with FCacheItems[Loop] do
- begin
- if LastAccess < FCacheItems[OldestIndex].LastAccess then
- OldestIndex := Loop;
- if ((CacheEnd - CacheStart) < FCacheItemSize) and (ReadPosition = CacheEnd + 1) then
- BeforeReadCacheIndex := Loop;
- if (ReadPositionEnd > CacheStart) and (ReadPositionEnd <= CacheEnd) then
- //Запомим индекскэша если он нашелся впервые, либо, если в найденном буфере
- //больше закэшированно данных чем в том что нашелся ранее
- if (EndCacheIndex = -1) or (CacheStart < FCacheItems[EndCacheIndex].CacheStart) then
- EndCacheIndex := Loop;
- //Проверим, есть ли в этом буфере какие-нибудь данные для нас
- if (ReadPosition >= CacheStart) and (ReadPosition < CacheEnd) then
- begin
- //Расчитаем с какой позиции в кэше хранятся данные длятекущего рида
- Offset := ReadPosition - CacheStart;
- //Расчитаем сколько данных из кэша можем взять
- ReadedFromCache := Min(Count, (CacheEnd - CacheStart) - Offset);
- //Собственно возьмем данные из кэша
- Move((Cache + Offset)^, Buffer, ReadedFromCache);
- //Прочтем оставшиеся данные из стрима, те которых не было в кэше
- Seek(ReadedFromCache, soCurrent);
- //Рекурсивно прочитаем остаток, что бы он либо из другого кэша взялся,
- //либо закэшировался дальше
- ReadedFromStream := Read(PByte(PByte(@Buffer) + ReadedFromCache)^, Count - ReadedFromCache);
- //Расчитам общее количество прочитанных байт
- Result := ReadedFromCache + ReadedFromStream;
- //Восстановим позицию, потому что в рекурсии она могла меняться
- FStream.Position := ReadPosition + Result;
- Exit;
- end;
- end;
- //Может быть с конца буфера что-то есть
- if EndCacheIndex <> -1 then
- with FCacheItems[EndCacheIndex] do
- begin
- //Расчитаем сколько данных из кэша можем взять
- ReadedFromCache := ReadPositionEnd - CacheStart;
- Offset := Count - ReadedFromCache;
- //Собственно возьмем данные из кэша
- Move(Cache^, (PByte(@Buffer) + Offset)^, ReadedFromCache);
- //А теперь прочитаем из стрима
- ReadedFromStream := Read(Buffer, Offset);
- //Расчитам общее количество прочитанных байт
- Result := ReadedFromStream + ReadedFromCache;
- FStream.Position := ReadPosition + Result;
- Exit;
- end;
- //Если данных в буфере не нашли, прочитаем их из стрима и закэшируем
- //Если уже есть кэш, в котором есть свободное место, и данные хранящиеся в нем
- //являются предварительными заканчиваются прямо перед
- if BeforeReadCacheIndex <> -1 then
- with FCacheItems[BeforeReadCacheIndex] do
- begin
- Result := FStream.Read(Buffer, Min(Count, FCacheItemSize - (CacheEnd - CacheStart)));
- if Result > 0 then
- begin
- //Закэшируем в новый буфер
- Move(Buffer, (Cache + (CacheEnd - CacheStart))^, Result);
- CacheEnd := CacheEnd + Result;
- Result := Result + Read(PByte(PByte(@Buffer) + Result)^, Count - Result);
- end;
- Exit;
- end;
- //Проситаем данные и перезапишим ими самый давно неиспользуемый кэш
- Result := FStream.Read(Buffer, Min(Count, FCacheItemSize));
- if Result > 0 then
- begin
- //Закэшируем в новый буфер
- FCacheItems[OldestIndex].CacheStart := ReadPosition;
- FCacheItems[OldestIndex].CacheEnd := ReadPosition + Result;
- Move(Buffer, FCacheItems[OldestIndex].Cache^, Result);
- //Если мы прочитали только размер буфера, дочитаем остаток
- if (Result = Integer(FCacheItemSize)) and (Result < Count) then
- Result := Result + Read(PByte(PByte(@Buffer) + Result)^, Count - Result);
- end;
- end;
- { TsuReadMapGeneratorProxyStream }
- constructor TsuReadMapGeneratorProxyStream.Create(AStream: TStream;
- const AMapFile: string; Ownership: TStreamOwnership);
- begin
- Assign(FMapFile, AMapFile);
- Rewrite(FMapFile);
- inherited Create(AStream, Ownership);
- end;
- destructor TsuReadMapGeneratorProxyStream.Destroy;
- begin
- Flush(FMapFile);
- Close(FMapFile);
- inherited;
- end;
- function TsuReadMapGeneratorProxyStream.Read(var Buffer;
- Count: Integer): Integer;
- begin
- Writeln(FMapFile, Format('%u,%u',[Position, Count]));
- Result := inherited;
- end;
- end.
Add Comment
Please, Sign In to add comment