| 1 | unit UBufferedFileStream;
|
|---|
| 2 |
|
|---|
| 3 | interface
|
|---|
| 4 |
|
|---|
| 5 | uses
|
|---|
| 6 | Classes, SysUtils;
|
|---|
| 7 |
|
|---|
| 8 | type
|
|---|
| 9 | TBufferedFileStream = class(TStream)
|
|---|
| 10 | private
|
|---|
| 11 | FFile: TFileStream;
|
|---|
| 12 | FBuffer: TMemoryStream;
|
|---|
| 13 | FBufferPosition: Integer;
|
|---|
| 14 | FBufferMaxSize: Integer;
|
|---|
| 15 | FPosition: Integer;
|
|---|
| 16 | FSize: Integer;
|
|---|
| 17 | FDoFlush: Boolean;
|
|---|
| 18 | procedure SetSize(NewSize: Longint); override;
|
|---|
| 19 | procedure SetSize(const NewSize: Int64); override;
|
|---|
| 20 | function GetBufferUsed: Integer;
|
|---|
| 21 | function GetSize: Int64; override;
|
|---|
| 22 | public
|
|---|
| 23 | constructor Create(const AFileName: string; Mode: Word); overload;
|
|---|
| 24 | constructor Create(const AFileName: string; Mode: Word; Rights: Cardinal); overload;
|
|---|
| 25 | destructor Destroy; override;
|
|---|
| 26 | function Read(var Buffer; Count: Longint): Longint; override;
|
|---|
| 27 | function Write(const Buffer; Count: Longint): Longint; override;
|
|---|
| 28 | function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;
|
|---|
| 29 | procedure Flush;
|
|---|
| 30 | property BufferMaxSize: Integer read FBufferMaxSize write FBufferMaxSize;
|
|---|
| 31 | property BufferUsed: Integer read GetBufferUsed;
|
|---|
| 32 | end;
|
|---|
| 33 |
|
|---|
| 34 | implementation
|
|---|
| 35 |
|
|---|
| 36 | { TBufferedFileStream }
|
|---|
| 37 |
|
|---|
| 38 | constructor TBufferedFileStream.Create(const AFileName: string; Mode: Word;
|
|---|
| 39 | Rights: Cardinal);
|
|---|
| 40 | begin
|
|---|
| 41 | FFile := TFileStream.Create(AFileName, Mode, Rights);
|
|---|
| 42 | FSize := FFile.Size;
|
|---|
| 43 | FBuffer := TMemoryStream.Create;
|
|---|
| 44 | FBufferMaxSize := 60000;
|
|---|
| 45 | FPosition := 0;
|
|---|
| 46 | end;
|
|---|
| 47 |
|
|---|
| 48 | constructor TBufferedFileStream.Create(const AFileName: string;
|
|---|
| 49 | Mode: Word);
|
|---|
| 50 | begin
|
|---|
| 51 | {$IFDEF MSWINDOWS}
|
|---|
| 52 | Create(AFilename, Mode, 0);
|
|---|
| 53 | {$ELSE}
|
|---|
| 54 | Create(AFilename, Mode, 0);
|
|---|
| 55 | {$ENDIF}
|
|---|
| 56 | end;
|
|---|
| 57 |
|
|---|
| 58 | destructor TBufferedFileStream.Destroy;
|
|---|
| 59 | begin
|
|---|
| 60 | Flush;
|
|---|
| 61 | FFile.Free;
|
|---|
| 62 | FBuffer.Free;
|
|---|
| 63 | inherited;
|
|---|
| 64 | end;
|
|---|
| 65 |
|
|---|
| 66 | procedure TBufferedFileStream.Flush;
|
|---|
| 67 | var
|
|---|
| 68 | BufferMemory: Pointer;
|
|---|
| 69 | WritedCount: Integer;
|
|---|
| 70 | begin
|
|---|
| 71 | if FDoFlush then begin
|
|---|
| 72 | // Write buffer to disk
|
|---|
| 73 | FFile.Position := FBufferPosition;
|
|---|
| 74 | BufferMemory := FBuffer.Memory;
|
|---|
| 75 | FBuffer.Position := 0;
|
|---|
| 76 | WritedCount := FFile.Write(BufferMemory^, FBuffer.Size);
|
|---|
| 77 | FBuffer.Size := 0;
|
|---|
| 78 | FDoFlush := False;
|
|---|
| 79 | FBufferPosition := FBufferPosition + WritedCount;
|
|---|
| 80 | end;
|
|---|
| 81 | end;
|
|---|
| 82 |
|
|---|
| 83 | function TBufferedFileStream.GetBufferUsed: Integer;
|
|---|
| 84 | begin
|
|---|
| 85 | Result := FBuffer.Size;
|
|---|
| 86 | end;
|
|---|
| 87 |
|
|---|
| 88 | function TBufferedFileStream.GetSize: Int64;
|
|---|
| 89 | begin
|
|---|
| 90 | Result := FSize;
|
|---|
| 91 | end;
|
|---|
| 92 |
|
|---|
| 93 | function TBufferedFileStream.Read(var Buffer; Count: Integer): Longint;
|
|---|
| 94 | var
|
|---|
| 95 | ReadedCount: Integer;
|
|---|
| 96 | TotalReadedCount: Integer;
|
|---|
| 97 | BufferMemory: Pointer;
|
|---|
| 98 | BufferPointer: Pointer;
|
|---|
| 99 | RequestRead: Integer;
|
|---|
| 100 | begin
|
|---|
| 101 | TotalReadedCount := 0;
|
|---|
| 102 | repeat
|
|---|
| 103 | BufferPointer := Pointer(Integer(Addr(Buffer)) + TotalReadedCount);
|
|---|
| 104 | RequestRead := Count - TotalReadedCount;
|
|---|
| 105 | ReadedCount := FBuffer.Read(BufferPointer^, RequestRead);
|
|---|
| 106 | Inc(FPosition, ReadedCount);
|
|---|
| 107 | Inc(TotalReadedCount, ReadedCount);
|
|---|
| 108 | if ReadedCount < RequestRead then begin
|
|---|
| 109 | if FDoFlush then Flush;
|
|---|
| 110 | // Not enough data in memory buffer, read next
|
|---|
| 111 | FBufferPosition := FFile.Position;
|
|---|
| 112 | FBuffer.Size := FBufferMaxSize; // Allocate max. buffer size
|
|---|
| 113 | BufferMemory := FBuffer.Memory;
|
|---|
| 114 | FBuffer.Position := 0;
|
|---|
| 115 | ReadedCount := FFile.Read(BufferMemory^, FBufferMaxSize); // Try to load max amount of data
|
|---|
| 116 | FBuffer.Size := ReadedCount; // Lower buffer size
|
|---|
| 117 | end;
|
|---|
| 118 | until (TotalReadedCount >= Count) or (Position = Size);
|
|---|
| 119 | Result := TotalReadedCount;
|
|---|
| 120 | end;
|
|---|
| 121 |
|
|---|
| 122 | function TBufferedFileStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
|
|---|
| 123 | begin
|
|---|
| 124 | case Origin of
|
|---|
| 125 | soBeginning: FPosition := Offset;
|
|---|
| 126 | soCurrent: Inc(FPosition, Offset);
|
|---|
| 127 | soEnd: FPosition := FFile.Size + Offset;
|
|---|
| 128 | end;
|
|---|
| 129 | if (FPosition > (FBufferPosition + FBuffer.Size)) or (FPosition < FBufferPosition) then begin
|
|---|
| 130 | FFile.Seek(FPosition, soFromBeginning);
|
|---|
| 131 | Flush;
|
|---|
| 132 | FBuffer.Clear;
|
|---|
| 133 | end else FBuffer.Position := FPosition - FBufferPosition;
|
|---|
| 134 |
|
|---|
| 135 | Result := FPosition;
|
|---|
| 136 | end;
|
|---|
| 137 |
|
|---|
| 138 | procedure TBufferedFileStream.SetSize(NewSize: Integer);
|
|---|
| 139 | begin
|
|---|
| 140 | FBuffer.Size := 0;
|
|---|
| 141 | inherited;
|
|---|
| 142 | end;
|
|---|
| 143 |
|
|---|
| 144 | procedure TBufferedFileStream.SetSize(const NewSize: Int64);
|
|---|
| 145 | begin
|
|---|
| 146 | inherited;
|
|---|
| 147 | FBuffer.Size := 0;
|
|---|
| 148 | end;
|
|---|
| 149 |
|
|---|
| 150 | function TBufferedFileStream.Write(const Buffer; Count: Integer): Longint;
|
|---|
| 151 | var
|
|---|
| 152 | WritedCount: Integer;
|
|---|
| 153 | TotalWritedCount: Integer;
|
|---|
| 154 | BufferMemory: Pointer;
|
|---|
| 155 | BufferPointer: Pointer;
|
|---|
| 156 | RequestWrite: Integer;
|
|---|
| 157 | begin
|
|---|
| 158 | TotalWritedCount := 0;
|
|---|
| 159 | repeat
|
|---|
| 160 | BufferPointer := Pointer(Integer(Addr(Buffer)) + TotalWritedCount);
|
|---|
| 161 | RequestWrite := Count - TotalWritedCount;
|
|---|
| 162 | if RequestWrite > (FBufferMaxSize - FBuffer.Position) then // Limit max buffer size
|
|---|
| 163 | WritedCount := FBufferMaxSize - FBuffer.Position
|
|---|
| 164 | else WritedCount := RequestWrite;
|
|---|
| 165 |
|
|---|
| 166 | WritedCount := FBuffer.Write(BufferPointer^, WritedCount);
|
|---|
| 167 | Inc(FPosition, WritedCount);
|
|---|
| 168 | if FPosition > FSize then FSize := FPosition;
|
|---|
| 169 | Inc(TotalWritedCount, WritedCount);
|
|---|
| 170 | FDoFlush := True;
|
|---|
| 171 |
|
|---|
| 172 | if (WritedCount < RequestWrite) and FDoFlush then Flush;
|
|---|
| 173 | until (TotalWritedCount >= Count);
|
|---|
| 174 | Result := TotalWritedCount;
|
|---|
| 175 | end;
|
|---|
| 176 |
|
|---|
| 177 | end.
|
|---|