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