1// LimitedStreams.cpp
2
3#include "StdAfx.h"
4
5#include <string.h>
6
7#include "LimitedStreams.h"
8
9STDMETHODIMP 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
30STDMETHODIMP 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
59STDMETHODIMP 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
76HRESULT 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
88STDMETHODIMP 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
133STDMETHODIMP 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
153STDMETHODIMP 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
195STDMETHODIMP 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
213STDMETHODIMP 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
240STDMETHODIMP 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
250STDMETHODIMP 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
277STDMETHODIMP 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
317STDMETHODIMP 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
334STDMETHODIMP 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
346STDMETHODIMP 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
363STDMETHODIMP CTailOutStream::SetSize(UInt64 newSize)
364{
365  _virtSize = newSize;
366  return Stream->SetSize(Offset + newSize);
367}
368