1// StreamObjects.cpp
2
3#include "StdAfx.h"
4
5#include "../../../C/Alloc.h"
6
7#include "StreamObjects.h"
8
9STDMETHODIMP CBufInStream::Read(void *data, UInt32 size, UInt32 *processedSize)
10{
11  if (processedSize)
12    *processedSize = 0;
13  if (size == 0)
14    return S_OK;
15  if (_pos > _size)
16    return E_FAIL;
17  size_t rem = _size - (size_t)_pos;
18  if (rem > size)
19    rem = (size_t)size;
20  memcpy(data, _data + (size_t)_pos, rem);
21  _pos += rem;
22  if (processedSize)
23    *processedSize = (UInt32)rem;
24  return S_OK;
25}
26
27STDMETHODIMP CBufInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
28{
29  switch(seekOrigin)
30  {
31    case STREAM_SEEK_SET: _pos = offset; break;
32    case STREAM_SEEK_CUR: _pos += offset; break;
33    case STREAM_SEEK_END: _pos = _size + offset; break;
34    default: return STG_E_INVALIDFUNCTION;
35  }
36  if (newPosition)
37    *newPosition = _pos;
38  return S_OK;
39}
40
41void CByteDynBuffer::Free()
42{
43  free(_buf);
44  _buf = 0;
45  _capacity = 0;
46}
47
48bool CByteDynBuffer::EnsureCapacity(size_t cap)
49{
50  if (cap <= _capacity)
51    return true;
52  size_t delta;
53  if (_capacity > 64)
54    delta = _capacity / 4;
55  else if (_capacity > 8)
56    delta = 16;
57  else
58    delta = 4;
59  cap = MyMax(_capacity + delta, cap);
60  Byte *buf = (Byte *)realloc(_buf, cap);
61  if (!buf)
62    return false;
63  _buf = buf;
64  _capacity = cap;
65  return true;
66}
67
68Byte *CDynBufSeqOutStream::GetBufPtrForWriting(size_t addSize)
69{
70  addSize += _size;
71  if (addSize < _size)
72    return NULL;
73  if (!_buffer.EnsureCapacity(addSize))
74    return NULL;
75  return (Byte *)_buffer + _size;
76}
77
78void CDynBufSeqOutStream::CopyToBuffer(CByteBuffer &dest) const
79{
80  dest.SetCapacity(_size);
81  memcpy(dest, _buffer, _size);
82}
83
84STDMETHODIMP CDynBufSeqOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
85{
86  if (processedSize)
87    *processedSize = 0;
88  if (size == 0)
89    return S_OK;
90  Byte *buf = GetBufPtrForWriting(size);
91  if (!buf)
92    return E_OUTOFMEMORY;
93  memcpy(buf, data, size);
94  UpdateSize(size);
95  if (processedSize)
96    *processedSize = size;
97  return S_OK;
98}
99
100STDMETHODIMP CBufPtrSeqOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
101{
102  size_t rem = _size - _pos;
103  if (rem > size)
104    rem = (size_t)size;
105  memcpy(_buffer + _pos, data, rem);
106  _pos += rem;
107  if (processedSize)
108    *processedSize = (UInt32)rem;
109  return (rem != 0 || size == 0) ? S_OK : E_FAIL;
110}
111
112STDMETHODIMP CSequentialOutStreamSizeCount::Write(const void *data, UInt32 size, UInt32 *processedSize)
113{
114  UInt32 realProcessedSize;
115  HRESULT result = _stream->Write(data, size, &realProcessedSize);
116  _size += realProcessedSize;
117  if (processedSize)
118    *processedSize = realProcessedSize;
119  return result;
120}
121
122static const UInt64 kEmptyTag = (UInt64)(Int64)-1;
123
124void CCachedInStream::Free()
125{
126  MyFree(_tags);
127  _tags = 0;
128  MidFree(_data);
129  _data = 0;
130}
131
132bool CCachedInStream::Alloc(unsigned blockSizeLog, unsigned numBlocksLog)
133{
134  unsigned sizeLog = blockSizeLog + numBlocksLog;
135  if (sizeLog >= sizeof(size_t) * 8)
136    return false;
137  size_t dataSize = (size_t)1 << sizeLog;
138  if (_data == 0 || dataSize != _dataSize)
139  {
140    MidFree(_data);
141    _data = (Byte *)MidAlloc(dataSize);
142    if (_data == 0)
143      return false;
144    _dataSize = dataSize;
145  }
146  if (_tags == 0 || numBlocksLog != _numBlocksLog)
147  {
148    MyFree(_tags);
149    _tags = (UInt64 *)MyAlloc(sizeof(UInt64) << numBlocksLog);
150    if (_tags == 0)
151      return false;
152    _numBlocksLog = numBlocksLog;
153  }
154  _blockSizeLog = blockSizeLog;
155  return true;
156}
157
158void CCachedInStream::Init(UInt64 size)
159{
160  _size = size;
161  _pos = 0;
162  size_t numBlocks = (size_t)1 << _numBlocksLog;
163  for (size_t i = 0; i < numBlocks; i++)
164    _tags[i] = kEmptyTag;
165}
166
167STDMETHODIMP CCachedInStream::Read(void *data, UInt32 size, UInt32 *processedSize)
168{
169  if (processedSize)
170    *processedSize = 0;
171  if (size == 0)
172    return S_OK;
173  if (_pos > _size)
174    return E_FAIL;
175
176  {
177    UInt64 rem = _size - _pos;
178    if (size > rem)
179      size = (UInt32)rem;
180  }
181
182  while (size != 0)
183  {
184    UInt64 cacheTag = _pos >> _blockSizeLog;
185    size_t cacheIndex = (size_t)cacheTag & (((size_t)1 << _numBlocksLog) - 1);
186    Byte *p = _data + (cacheIndex << _blockSizeLog);
187    if (_tags[cacheIndex] != cacheTag)
188    {
189      UInt64 remInBlock = _size - (cacheTag << _blockSizeLog);
190      size_t blockSize = (size_t)1 << _blockSizeLog;
191      if (blockSize > remInBlock)
192        blockSize = (size_t)remInBlock;
193      RINOK(ReadBlock(cacheTag, p, blockSize));
194      _tags[cacheIndex] = cacheTag;
195    }
196    size_t offset = (size_t)_pos & (((size_t)1 << _blockSizeLog) - 1);
197    UInt32 cur = (UInt32)MyMin(((size_t)1 << _blockSizeLog) - offset, (size_t)size);
198    memcpy(data, p + offset, cur);
199    if (processedSize)
200      *processedSize += cur;
201    data = (void *)((const Byte *)data + cur);
202    _pos += cur;
203    size -= cur;
204  }
205
206  return S_OK;
207}
208
209STDMETHODIMP CCachedInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
210{
211  switch(seekOrigin)
212  {
213    case STREAM_SEEK_SET: _pos = offset; break;
214    case STREAM_SEEK_CUR: _pos = _pos + offset; break;
215    case STREAM_SEEK_END: _pos = _size + offset; break;
216    default: return STG_E_INVALIDFUNCTION;
217  }
218  if (newPosition != 0)
219    *newPosition = _pos;
220  return S_OK;
221}
222