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