forked from mikerabat/mrmath
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBufferedStream.pas
172 lines (143 loc) · 5.03 KB
/
BufferedStream.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// ###################################################################
// #### This file is part of the mathematics library project, and is
// #### offered under the licence agreement described on
// #### http://www.mrsoft.org/
// ####
// #### Copyright:(c) 2011, Michael R. . All rights reserved.
// ###################################################################
unit BufferedStream;
// ###############################################
// #### Buffered stream reading
// ###############################################
interface
uses SysUtils, Classes;
// ###############################################
// #### Reads blockwise from stream
// This class speedups the access to files
// by reading blocksize bytes from it and handle the bytewise reading
// from a buffer
type
TBufferedStream = class(TStream)
private
fStream : TStream;
fBuffer : array of Byte;
fActPos : PByte;
fBytesLeft : integer;
fBlockSize: integer;
fStartPos : int64;
fBytesRead : int64;
fReadBlockSize : integer;
function InternalRead(var Buf; BufSize : integer) : integer;
public
function Read(var Buf; BufSize : integer) : integer; override;
function Write(const Buffer; Count: Longint): Longint; override;
function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;
property BlockSize : integer read fBlockSize;
constructor Create(Stream : TStream; aBlockSize : integer);
destructor Destroy; override;
end;
implementation
uses Math;
{ TBufferedStream }
constructor TBufferedStream.Create(Stream: TStream; aBlockSize: integer);
begin
inherited Create;
assert(Assigned(stream), 'Stream may not be nil');
assert(BlockSize > 0, 'Error Blocksize must be larger than 0');
fBlockSize := aBlockSize;
SetLength(fBuffer, aBlockSize);
fStream := Stream;
fActPos := @fBuffer[0];
fBytesLeft := 0;
fStartPos := Stream.Position;
fBytesRead := 0;
fReadBlockSize := 0;
end;
destructor TBufferedStream.Destroy;
begin
{$IF DEFINED(FPC) or (CompilerVersion <= 21)}
fStream.Seek(fStartPos + fBytesRead, soFromBeginning);
{$ELSE}
fStream.Seek(fStartPos + fBytesRead, soBeginning);
{$IFEND}
inherited;
end;
function TBufferedStream.InternalRead(var Buf; BufSize: integer) : integer;
var toBuf : PByte;
bytesToRead : integer;
begin
Result := 0;
toBuf := @Buf;
// #################################################
// #### Read from stream
while BufSize > fBytesLeft do
begin
// read a block of data
if fBytesLeft > 0 then
begin
Move(fActPos^, toBuf^, fBytesLeft);
inc(toBuf, fBytesLeft);
inc(fBytesRead, fBytesLeft);
inc(Result, fBytesLeft);
dec(BufSize, fBytesLeft);
fBytesLeft := 0;
end;
fActPos := @fBuffer[0];
fBytesLeft := fStream.Read(fBuffer[0], fBlockSize);
fReadBlockSize := fBytesLeft;
if fBytesLeft < fBlockSize then
break;
end;
// #################################################
// #### Read from the buffer
bytesToRead := Min(fBytesLeft, BufSize);
Move(fActPos^, toBuf^, bytesToRead);
inc(fActPos, bytesToRead);
inc(Result, bytesToRead);
inc(fBytesRead, bytesToRead);
dec(fBytesLeft, bytesToRead);
end;
function TBufferedStream.Read(var Buf; BufSize: integer): integer;
begin
Result := InternalRead(Buf, BufSize);
end;
function TBufferedStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
var NewPosition : Int64;
actBlockStart : Int64;
begin
case origin of
soBeginning: NewPosition := Offset;
soCurrent: NewPosition := fBytesRead + Offset;
soEnd: NewPosition := fStream.Size + Offset;
else
NewPosition := 0;
end;
// check if the new position is within the actual buffer:
actBlockStart := fstream.Position - fReadBlockSize;
if (NewPosition >= actBlockStart) and (NewPosition < actBlockStart + fReadBlockSize) then
begin
Result := NewPosition;
fBytesRead := NewPosition - actBlockStart;
fBytesLeft := fReadBlockSize - (NewPosition - actBlockStart);
fActPos := @fBuffer[0];
inc(fActPos, fReadBlockSize - fBytesLeft);
end
else
begin
// clear and wait until the next block is read
fBytesRead := NewPosition;
fBytesLeft := 0;
{$IF DEFINED(FPC) or (CompilerVersion <= 21)}
Result := fStream.Seek(NewPosition, soFromBeginning);
{$ELSE}
Result := fStream.Seek(NewPosition, soBeginning);
{$IFEND}
fReadBlockSize := 0;
fActPos := @fBuffer[0];
end;
end;
function TBufferedStream.Write(const Buffer; Count: Integer): Longint;
begin
raise Exception.Create('Writes not implemented');
end;
end.