1 // LimitedStreams.cpp
2
3 #include "StdAfx.h"
4
5 #include <string.h>
6
7 #include "LimitedStreams.h"
8
Read(void * data,UInt32 size,UInt32 * processedSize)9 STDMETHODIMP CLimitedSequentialInStream::Read(void *data, UInt32 size, UInt32 *processedSize)
10 {
11 UInt32 realProcessedSize = 0;
12 {
13 const UInt64 rem = _size - _pos;
14 if (size > rem)
15 size = (UInt32)rem;
16 }
17 HRESULT result = S_OK;
18 if (size != 0)
19 {
20 result = _stream->Read(data, size, &realProcessedSize);
21 _pos += realProcessedSize;
22 if (realProcessedSize == 0)
23 _wasFinished = true;
24 }
25 if (processedSize)
26 *processedSize = realProcessedSize;
27 return result;
28 }
29
Read(void * data,UInt32 size,UInt32 * processedSize)30 STDMETHODIMP CLimitedInStream::Read(void *data, UInt32 size, UInt32 *processedSize)
31 {
32 if (processedSize)
33 *processedSize = 0;
34 if (_virtPos >= _size)
35 {
36 // 9.31: Fixed. Windows doesn't return error in ReadFile and IStream->Read in that case.
37 return S_OK;
38 // return (_virtPos == _size) ? S_OK: E_FAIL; // ERROR_HANDLE_EOF
39 }
40 {
41 const UInt64 rem = _size - _virtPos;
42 if (size > rem)
43 size = (UInt32)rem;
44 }
45 UInt64 newPos = _startOffset + _virtPos;
46 if (newPos != _physPos)
47 {
48 _physPos = newPos;
49 RINOK(SeekToPhys());
50 }
51 HRESULT res = _stream->Read(data, size, &size);
52 if (processedSize)
53 *processedSize = size;
54 _physPos += size;
55 _virtPos += size;
56 return res;
57 }
58
Seek(Int64 offset,UInt32 seekOrigin,UInt64 * newPosition)59 STDMETHODIMP CLimitedInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
60 {
61 switch (seekOrigin)
62 {
63 case STREAM_SEEK_SET: break;
64 case STREAM_SEEK_CUR: offset += _virtPos; break;
65 case STREAM_SEEK_END: offset += _size; break;
66 default: return STG_E_INVALIDFUNCTION;
67 }
68 if (offset < 0)
69 return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
70 _virtPos = offset;
71 if (newPosition)
72 *newPosition = _virtPos;
73 return S_OK;
74 }
75
CreateLimitedInStream(IInStream * inStream,UInt64 pos,UInt64 size,ISequentialInStream ** resStream)76 HRESULT CreateLimitedInStream(IInStream *inStream, UInt64 pos, UInt64 size, ISequentialInStream **resStream)
77 {
78 *resStream = 0;
79 CLimitedInStream *streamSpec = new CLimitedInStream;
80 CMyComPtr<ISequentialInStream> streamTemp = streamSpec;
81 streamSpec->SetStream(inStream);
82 RINOK(streamSpec->InitAndSeek(pos, size));
83 streamSpec->SeekToStart();
84 *resStream = streamTemp.Detach();
85 return S_OK;
86 }
87
Read(void * data,UInt32 size,UInt32 * processedSize)88 STDMETHODIMP CClusterInStream::Read(void *data, UInt32 size, UInt32 *processedSize)
89 {
90 if (processedSize)
91 *processedSize = 0;
92 if (_virtPos >= Size)
93 return S_OK;
94 {
95 UInt64 rem = Size - _virtPos;
96 if (size > rem)
97 size = (UInt32)rem;
98 }
99 if (size == 0)
100 return S_OK;
101
102 if (_curRem == 0)
103 {
104 const UInt32 blockSize = (UInt32)1 << BlockSizeLog;
105 const UInt32 virtBlock = (UInt32)(_virtPos >> BlockSizeLog);
106 const UInt32 offsetInBlock = (UInt32)_virtPos & (blockSize - 1);
107 const UInt32 phyBlock = Vector[virtBlock];
108
109 UInt64 newPos = StartOffset + ((UInt64)phyBlock << BlockSizeLog) + offsetInBlock;
110 if (newPos != _physPos)
111 {
112 _physPos = newPos;
113 RINOK(SeekToPhys());
114 }
115
116 _curRem = blockSize - offsetInBlock;
117
118 for (int i = 1; i < 64 && (virtBlock + i) < (UInt32)Vector.Size() && phyBlock + i == Vector[virtBlock + i]; i++)
119 _curRem += (UInt32)1 << BlockSizeLog;
120 }
121
122 if (size > _curRem)
123 size = _curRem;
124 HRESULT res = Stream->Read(data, size, &size);
125 if (processedSize)
126 *processedSize = size;
127 _physPos += size;
128 _virtPos += size;
129 _curRem -= size;
130 return res;
131 }
132
Seek(Int64 offset,UInt32 seekOrigin,UInt64 * newPosition)133 STDMETHODIMP CClusterInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
134 {
135 switch (seekOrigin)
136 {
137 case STREAM_SEEK_SET: break;
138 case STREAM_SEEK_CUR: offset += _virtPos; break;
139 case STREAM_SEEK_END: offset += Size; break;
140 default: return STG_E_INVALIDFUNCTION;
141 }
142 if (offset < 0)
143 return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
144 if (_virtPos != (UInt64)offset)
145 _curRem = 0;
146 _virtPos = offset;
147 if (newPosition)
148 *newPosition = offset;
149 return S_OK;
150 }
151
152
Read(void * data,UInt32 size,UInt32 * processedSize)153 STDMETHODIMP CExtentsStream::Read(void *data, UInt32 size, UInt32 *processedSize)
154 {
155 if (processedSize)
156 *processedSize = 0;
157 if (_virtPos >= Extents.Back().Virt)
158 return S_OK;
159 if (size == 0)
160 return S_OK;
161
162 unsigned left = 0, right = Extents.Size() - 1;
163 for (;;)
164 {
165 unsigned mid = (left + right) / 2;
166 if (mid == left)
167 break;
168 if (_virtPos < Extents[mid].Virt)
169 right = mid;
170 else
171 left = mid;
172 }
173
174 const CSeekExtent &extent = Extents[left];
175 UInt64 phyPos = extent.Phy + (_virtPos - extent.Virt);
176 if (_needStartSeek || _phyPos != phyPos)
177 {
178 _needStartSeek = false;
179 _phyPos = phyPos;
180 RINOK(SeekToPhys());
181 }
182
183 UInt64 rem = Extents[left + 1].Virt - _virtPos;
184 if (size > rem)
185 size = (UInt32)rem;
186
187 HRESULT res = Stream->Read(data, size, &size);
188 _phyPos += size;
189 _virtPos += size;
190 if (processedSize)
191 *processedSize = size;
192 return res;
193 }
194
Seek(Int64 offset,UInt32 seekOrigin,UInt64 * newPosition)195 STDMETHODIMP CExtentsStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
196 {
197 switch (seekOrigin)
198 {
199 case STREAM_SEEK_SET: break;
200 case STREAM_SEEK_CUR: offset += _virtPos; break;
201 case STREAM_SEEK_END: offset += Extents.Back().Virt; break;
202 default: return STG_E_INVALIDFUNCTION;
203 }
204 if (offset < 0)
205 return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
206 _virtPos = offset;
207 if (newPosition)
208 *newPosition = _virtPos;
209 return S_OK;
210 }
211
212
Write(const void * data,UInt32 size,UInt32 * processedSize)213 STDMETHODIMP CLimitedSequentialOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
214 {
215 HRESULT result = S_OK;
216 if (processedSize)
217 *processedSize = 0;
218 if (size > _size)
219 {
220 if (_size == 0)
221 {
222 _overflow = true;
223 if (!_overflowIsAllowed)
224 return E_FAIL;
225 if (processedSize)
226 *processedSize = size;
227 return S_OK;
228 }
229 size = (UInt32)_size;
230 }
231 if (_stream)
232 result = _stream->Write(data, size, &size);
233 _size -= size;
234 if (processedSize)
235 *processedSize = size;
236 return result;
237 }
238
239
Read(void * data,UInt32 size,UInt32 * processedSize)240 STDMETHODIMP CTailInStream::Read(void *data, UInt32 size, UInt32 *processedSize)
241 {
242 UInt32 cur;
243 HRESULT res = Stream->Read(data, size, &cur);
244 if (processedSize)
245 *processedSize = cur;
246 _virtPos += cur;
247 return res;
248 }
249
Seek(Int64 offset,UInt32 seekOrigin,UInt64 * newPosition)250 STDMETHODIMP CTailInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
251 {
252 switch (seekOrigin)
253 {
254 case STREAM_SEEK_SET: break;
255 case STREAM_SEEK_CUR: offset += _virtPos; break;
256 case STREAM_SEEK_END:
257 {
258 UInt64 pos = 0;
259 RINOK(Stream->Seek(offset, STREAM_SEEK_END, &pos));
260 if (pos < Offset)
261 return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
262 _virtPos = pos - Offset;
263 if (newPosition)
264 *newPosition = _virtPos;
265 return S_OK;
266 }
267 default: return STG_E_INVALIDFUNCTION;
268 }
269 if (offset < 0)
270 return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
271 _virtPos = offset;
272 if (newPosition)
273 *newPosition = _virtPos;
274 return Stream->Seek(Offset + _virtPos, STREAM_SEEK_SET, NULL);
275 }
276
Read(void * data,UInt32 size,UInt32 * processedSize)277 STDMETHODIMP CLimitedCachedInStream::Read(void *data, UInt32 size, UInt32 *processedSize)
278 {
279 if (processedSize)
280 *processedSize = 0;
281 if (_virtPos >= _size)
282 {
283 // 9.31: Fixed. Windows doesn't return error in ReadFile and IStream->Read in that case.
284 return S_OK;
285 // return (_virtPos == _size) ? S_OK: E_FAIL; // ERROR_HANDLE_EOF
286 }
287 UInt64 rem = _size - _virtPos;
288 if (rem < size)
289 size = (UInt32)rem;
290
291 UInt64 newPos = _startOffset + _virtPos;
292 UInt64 offsetInCache = newPos - _cachePhyPos;
293 HRESULT res = S_OK;
294 if (newPos >= _cachePhyPos &&
295 offsetInCache <= _cacheSize &&
296 size <= _cacheSize - (size_t)offsetInCache)
297 {
298 if (size != 0)
299 memcpy(data, _cache + (size_t)offsetInCache, size);
300 }
301 else
302 {
303 if (newPos != _physPos)
304 {
305 _physPos = newPos;
306 RINOK(SeekToPhys());
307 }
308 res = _stream->Read(data, size, &size);
309 _physPos += size;
310 }
311 if (processedSize)
312 *processedSize = size;
313 _virtPos += size;
314 return res;
315 }
316
Seek(Int64 offset,UInt32 seekOrigin,UInt64 * newPosition)317 STDMETHODIMP CLimitedCachedInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
318 {
319 switch (seekOrigin)
320 {
321 case STREAM_SEEK_SET: break;
322 case STREAM_SEEK_CUR: offset += _virtPos; break;
323 case STREAM_SEEK_END: offset += _size; break;
324 default: return STG_E_INVALIDFUNCTION;
325 }
326 if (offset < 0)
327 return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
328 _virtPos = offset;
329 if (newPosition)
330 *newPosition = _virtPos;
331 return S_OK;
332 }
333
Write(const void * data,UInt32 size,UInt32 * processedSize)334 STDMETHODIMP CTailOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
335 {
336 UInt32 cur;
337 HRESULT res = Stream->Write(data, size, &cur);
338 if (processedSize)
339 *processedSize = cur;
340 _virtPos += cur;
341 if (_virtSize < _virtPos)
342 _virtSize = _virtPos;
343 return res;
344 }
345
Seek(Int64 offset,UInt32 seekOrigin,UInt64 * newPosition)346 STDMETHODIMP CTailOutStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
347 {
348 switch (seekOrigin)
349 {
350 case STREAM_SEEK_SET: break;
351 case STREAM_SEEK_CUR: offset += _virtPos; break;
352 case STREAM_SEEK_END: offset += _virtSize; break;
353 default: return STG_E_INVALIDFUNCTION;
354 }
355 if (offset < 0)
356 return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
357 _virtPos = offset;
358 if (newPosition)
359 *newPosition = _virtPos;
360 return Stream->Seek(Offset + _virtPos, STREAM_SEEK_SET, NULL);
361 }
362
SetSize(UInt64 newSize)363 STDMETHODIMP CTailOutStream::SetSize(UInt64 newSize)
364 {
365 _virtSize = newSize;
366 return Stream->SetSize(Offset + newSize);
367 }
368