summaryrefslogtreecommitdiffstats
path: root/src/libs/7zip/win/CPP/7zip/Common/LimitedStreams.cpp
blob: 1837e32012af46254450ebf06997c2d900e83d25 (plain)
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
// LimitedStreams.cpp

#include "StdAfx.h"

#include "LimitedStreams.h"
#include "../../Common/Defs.h"

STDMETHODIMP CLimitedSequentialInStream::Read(void *data, UInt32 size, UInt32 *processedSize)
{
  UInt32 realProcessedSize = 0;
  UInt32 sizeToRead = (UInt32)MyMin((_size - _pos), (UInt64)size);
  HRESULT result = S_OK;
  if (sizeToRead > 0)
  {
    result = _stream->Read(data, sizeToRead, &realProcessedSize);
    _pos += realProcessedSize;
    if (realProcessedSize == 0)
      _wasFinished = true;
  }
  if (processedSize != NULL)
    *processedSize = realProcessedSize;
  return result;
}

STDMETHODIMP CLimitedInStream::Read(void *data, UInt32 size, UInt32 *processedSize)
{
  if (processedSize != NULL)
    *processedSize = 0;
  if (_virtPos >= _size)
    return (_virtPos == _size) ? S_OK: E_FAIL;
  UInt64 rem = _size - _virtPos;
  if (rem < size)
    size = (UInt32)rem;
  UInt64 newPos = _startOffset + _virtPos;
  if (newPos != _physPos)
  {
    _physPos = newPos;
    RINOK(SeekToPhys());
  }
  HRESULT res = _stream->Read(data, size, &size);
  if (processedSize != NULL)
    *processedSize = size;
  _physPos += size;
  _virtPos += size;
  return res;
}

STDMETHODIMP CLimitedInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
{
  switch(seekOrigin)
  {
    case STREAM_SEEK_SET: _virtPos = offset; break;
    case STREAM_SEEK_CUR: _virtPos += offset; break;
    case STREAM_SEEK_END: _virtPos = _size + offset; break;
    default: return STG_E_INVALIDFUNCTION;
  }
  if (newPosition)
    *newPosition = _virtPos;
  return S_OK;
}

STDMETHODIMP CClusterInStream::Read(void *data, UInt32 size, UInt32 *processedSize)
{
  if (processedSize != NULL)
    *processedSize = 0;
  if (_virtPos >= Size)
    return (_virtPos == Size) ? S_OK: E_FAIL;

  if (_curRem == 0)
  {
    UInt32 blockSize = (UInt32)1 << BlockSizeLog;
    UInt32 virtBlock = (UInt32)(_virtPos >> BlockSizeLog);
    UInt32 offsetInBlock = (UInt32)_virtPos & (blockSize - 1);
    UInt32 phyBlock = Vector[virtBlock];
    UInt64 newPos = StartOffset + ((UInt64)phyBlock << BlockSizeLog) + offsetInBlock;
    if (newPos != _physPos)
    {
      _physPos = newPos;
      RINOK(SeekToPhys());
    }
    _curRem = blockSize - offsetInBlock;
    for (int i = 1; i < 64 && (virtBlock + i) < (UInt32)Vector.Size() && phyBlock + i == Vector[virtBlock + i]; i++)
      _curRem += (UInt32)1 << BlockSizeLog;
    UInt64 rem = Size - _virtPos;
    if (_curRem > rem)
      _curRem = (UInt32)rem;
  }
  if (size > _curRem)
    size = _curRem;
  HRESULT res = Stream->Read(data, size, &size);
  if (processedSize != NULL)
    *processedSize = size;
  _physPos += size;
  _virtPos += size;
  _curRem -= size;
  return res;
}
 
STDMETHODIMP CClusterInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
{
  UInt64 newVirtPos = offset;
  switch(seekOrigin)
  {
    case STREAM_SEEK_SET: break;
    case STREAM_SEEK_CUR: newVirtPos += _virtPos; break;
    case STREAM_SEEK_END: newVirtPos += Size; break;
    default: return STG_E_INVALIDFUNCTION;
  }
  if (_virtPos != newVirtPos)
    _curRem = 0;
  _virtPos = newVirtPos;
  if (newPosition)
    *newPosition = newVirtPos;
  return S_OK;
}


HRESULT CreateLimitedInStream(IInStream *inStream, UInt64 pos, UInt64 size, ISequentialInStream **resStream)
{
  *resStream = 0;
  CLimitedInStream *streamSpec = new CLimitedInStream;
  CMyComPtr<ISequentialInStream> streamTemp = streamSpec;
  streamSpec->SetStream(inStream);
  RINOK(streamSpec->InitAndSeek(pos, size));
  streamSpec->SeekToStart();
  *resStream = streamTemp.Detach();
  return S_OK;
}

STDMETHODIMP CLimitedSequentialOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
{
  HRESULT result = S_OK;
  if (processedSize != NULL)
    *processedSize = 0;
  if (size > _size)
  {
    if (_size == 0)
    {
      _overflow = true;
      if (!_overflowIsAllowed)
        return E_FAIL;
      if (processedSize != NULL)
        *processedSize = size;
      return S_OK;
    }
    size = (UInt32)_size;
  }
  if (_stream)
    result = _stream->Write(data, size, &size);
  _size -= size;
  if (processedSize != NULL)
    *processedSize = size;
  return result;
}