1// InOutTempBuffer.cpp
2
3#include "StdAfx.h"
4
5#include "../../../C/Alloc.h"
6
7#include "InOutTempBuffer.h"
8
9#include "StreamUtils.h"
10
11#ifdef USE_InOutTempBuffer_FILE
12
13#include "../../../C/7zCrc.h"
14
15#define kTempFilePrefixString FTEXT("7zt")
16/*
17  Total buffer size limit, if we use temp file scheme:
18    32-bit:  16 MiB = 1 MiB *   16 buffers
19    64-bit:   4 GiB = 1 MiB * 4096 buffers
20*/
21static const size_t kNumBufsMax = (size_t)1 << (sizeof(size_t) * 2 - 4);
22
23#endif
24
25static const size_t kBufSize = (size_t)1 << 20;
26
27
28CInOutTempBuffer::CInOutTempBuffer():
29    _size(0),
30    _bufs(NULL),
31    _numBufs(0),
32    _numFilled(0)
33{
34 #ifdef USE_InOutTempBuffer_FILE
35  _tempFile_Created = false;
36  _useMemOnly = false;
37  _crc = CRC_INIT_VAL;
38 #endif
39}
40
41CInOutTempBuffer::~CInOutTempBuffer()
42{
43  for (size_t i = 0; i < _numBufs; i++)
44    MyFree(_bufs[i]);
45  MyFree(_bufs);
46}
47
48
49void *CInOutTempBuffer::GetBuf(size_t index)
50{
51  if (index >= _numBufs)
52  {
53    const size_t num = (_numBufs == 0 ? 16 : _numBufs * 2);
54    void **p = (void **)MyRealloc(_bufs, num * sizeof(void *));
55    if (!p)
56      return NULL;
57    _bufs = p;
58    memset(p + _numBufs, 0, (num - _numBufs) * sizeof(void *));
59    _numBufs = num;
60  }
61
62  void *buf = _bufs[index];
63  if (!buf)
64  {
65    buf = MyAlloc(kBufSize);
66    if (buf)
67      _bufs[index] = buf;
68  }
69  return buf;
70}
71
72
73HRESULT CInOutTempBuffer::Write_HRESULT(const void *data, UInt32 size)
74{
75  if (size == 0)
76    return S_OK;
77
78 #ifdef USE_InOutTempBuffer_FILE
79  if (!_tempFile_Created)
80 #endif
81  for (;;)  // loop for additional attemp to allocate memory after file creation error
82  {
83   #ifdef USE_InOutTempBuffer_FILE
84    bool allocError = false;
85   #endif
86
87    for (;;)  // loop for writing to buffers
88    {
89      const size_t index = (size_t)(_size / kBufSize);
90
91     #ifdef USE_InOutTempBuffer_FILE
92      if (index >= kNumBufsMax && !_useMemOnly)
93        break;
94     #endif
95
96      void *buf = GetBuf(index);
97      if (!buf)
98      {
99       #ifdef USE_InOutTempBuffer_FILE
100        if (!_useMemOnly)
101        {
102          allocError = true;
103          break;
104        }
105       #endif
106        return E_OUTOFMEMORY;
107      }
108
109      const size_t offset = (size_t)(_size) & (kBufSize - 1);
110      size_t cur = kBufSize - offset;
111      if (cur > size)
112        cur = size;
113      memcpy((Byte *)buf + offset, data, cur);
114      _size += cur;
115      if (index >= _numFilled)
116        _numFilled = index + 1;
117      data = (const void *)((const Byte *)data + cur);
118      size -= (UInt32)cur;
119      if (size == 0)
120        return S_OK;
121    }
122
123   #ifdef USE_InOutTempBuffer_FILE
124   #ifndef _WIN32
125    _outFile.mode_for_Create = 0600;  // only owner will have the rights to access this file
126   #endif
127    if (_tempFile.CreateRandomInTempFolder(kTempFilePrefixString, &_outFile))
128    {
129      _tempFile_Created = true;
130      break;
131    }
132    _useMemOnly = true;
133    if (allocError)
134      return GetLastError_noZero_HRESULT();
135   #endif
136  }
137
138 #ifdef USE_InOutTempBuffer_FILE
139  if (!_outFile.WriteFull(data, size))
140    return GetLastError_noZero_HRESULT();
141  _crc = CrcUpdate(_crc, data, size);
142  _size += size;
143  return S_OK;
144 #endif
145}
146
147
148HRESULT CInOutTempBuffer::WriteToStream(ISequentialOutStream *stream)
149{
150  UInt64 rem = _size;
151  // if (rem == 0) return S_OK;
152
153  const size_t numFilled = _numFilled;
154  _numFilled = 0;
155
156  for (size_t i = 0; i < numFilled; i++)
157  {
158    if (rem == 0)
159      return E_FAIL;
160    size_t cur = kBufSize;
161    if (cur > rem)
162      cur = (size_t)rem;
163    RINOK(WriteStream(stream, _bufs[i], cur))
164    rem -= cur;
165   #ifdef USE_InOutTempBuffer_FILE
166    // we will use _bufs[0] later for writing from temp file
167    if (i != 0 || !_tempFile_Created)
168   #endif
169    {
170      MyFree(_bufs[i]);
171      _bufs[i] = NULL;
172    }
173  }
174
175
176 #ifdef USE_InOutTempBuffer_FILE
177
178  if (rem == 0)
179    return _tempFile_Created ? E_FAIL : S_OK;
180
181  if (!_tempFile_Created)
182    return E_FAIL;
183
184  if (!_outFile.Close())
185    return GetLastError_noZero_HRESULT();
186
187  HRESULT hres;
188  void *buf = GetBuf(0); // index
189  if (!buf)
190    hres = E_OUTOFMEMORY;
191  else
192  {
193    NWindows::NFile::NIO::CInFile inFile;
194    if (!inFile.Open(_tempFile.GetPath()))
195      hres = GetLastError_noZero_HRESULT();
196    else
197    {
198      UInt32 crc = CRC_INIT_VAL;
199      for (;;)
200      {
201        size_t processed;
202        if (!inFile.ReadFull(buf, kBufSize, processed))
203        {
204          hres = GetLastError_noZero_HRESULT();
205          break;
206        }
207        if (processed == 0)
208        {
209          // we compare crc without CRC_GET_DIGEST
210          hres = (_crc == crc ? S_OK : E_FAIL);
211          break;
212        }
213        size_t n = processed;
214        if (n > rem)
215          n = (size_t)rem;
216        hres = WriteStream(stream, buf, n);
217        if (hres != S_OK)
218          break;
219        crc = CrcUpdate(crc, buf, n);
220        rem -= n;
221        if (n != processed)
222        {
223          hres = E_FAIL;
224          break;
225        }
226      }
227    }
228  }
229
230  // _tempFile.DisableDeleting(); // for debug
231  _tempFile.Remove();
232  RINOK(hres)
233
234 #endif
235
236  return rem == 0 ? S_OK : E_FAIL;
237}
238