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