• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
21 static const size_t kNumBufsMax = (size_t)1 << (sizeof(size_t) * 2 - 4);
22 
23 #endif
24 
25 static const size_t kBufSize = (size_t)1 << 20;
26 
27 
CInOutTempBuffer()28 CInOutTempBuffer::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 
~CInOutTempBuffer()41 CInOutTempBuffer::~CInOutTempBuffer()
42 {
43   for (size_t i = 0; i < _numBufs; i++)
44     MyFree(_bufs[i]);
45   MyFree(_bufs);
46 }
47 
48 
GetBuf(size_t index)49 void *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 
Write_HRESULT(const void * data,UInt32 size)73 HRESULT 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 
WriteToStream(ISequentialOutStream * stream)148 HRESULT 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