7zEncode.cpp revision baa3858d3f5d128a5c8466b700098109edcad5f2
1// 7zEncode.cpp
2
3#include "StdAfx.h"
4
5#include "../../Common/CreateCoder.h"
6#include "../../Common/FilterCoder.h"
7#include "../../Common/LimitedStreams.h"
8#include "../../Common/InOutTempBuffer.h"
9#include "../../Common/ProgressUtils.h"
10#include "../../Common/StreamObjects.h"
11
12#include "7zEncode.h"
13#include "7zSpecStream.h"
14
15static const UInt64 k_Delta = 0x03;
16static const UInt64 k_BCJ = 0x03030103;
17static const UInt64 k_BCJ2 = 0x0303011B;
18
19namespace NArchive {
20namespace N7z {
21
22static void ConvertBindInfoToFolderItemInfo(const NCoderMixer::CBindInfo &bindInfo,
23    const CRecordVector<CMethodId> decompressionMethods,
24    CFolder &folder)
25{
26  folder.Coders.Clear();
27  // bindInfo.CoderMethodIDs.Clear();
28  // folder.OutStreams.Clear();
29  folder.PackStreams.Clear();
30  folder.BindPairs.Clear();
31  int i;
32  for (i = 0; i < bindInfo.BindPairs.Size(); i++)
33  {
34    CBindPair bindPair;
35    bindPair.InIndex = bindInfo.BindPairs[i].InIndex;
36    bindPair.OutIndex = bindInfo.BindPairs[i].OutIndex;
37    folder.BindPairs.Add(bindPair);
38  }
39  for (i = 0; i < bindInfo.Coders.Size(); i++)
40  {
41    CCoderInfo coderInfo;
42    const NCoderMixer::CCoderStreamsInfo &coderStreamsInfo = bindInfo.Coders[i];
43    coderInfo.NumInStreams = coderStreamsInfo.NumInStreams;
44    coderInfo.NumOutStreams = coderStreamsInfo.NumOutStreams;
45    coderInfo.MethodID = decompressionMethods[i];
46    folder.Coders.Add(coderInfo);
47  }
48  for (i = 0; i < bindInfo.InStreams.Size(); i++)
49    folder.PackStreams.Add(bindInfo.InStreams[i]);
50}
51
52HRESULT CEncoder::CreateMixerCoder(
53    DECL_EXTERNAL_CODECS_LOC_VARS
54    const UInt64 *inSizeForReduce)
55{
56  _mixerCoderSpec = new NCoderMixer::CCoderMixer2MT;
57  _mixerCoder = _mixerCoderSpec;
58  RINOK(_mixerCoderSpec->SetBindInfo(_bindInfo));
59  for (int i = 0; i < _options.Methods.Size(); i++)
60  {
61    const CMethodFull &methodFull = _options.Methods[i];
62    _codersInfo.Add(CCoderInfo());
63    CCoderInfo &encodingInfo = _codersInfo.Back();
64    encodingInfo.MethodID = methodFull.Id;
65    CMyComPtr<ICompressCoder> encoder;
66    CMyComPtr<ICompressCoder2> encoder2;
67
68
69    RINOK(CreateCoder(
70        EXTERNAL_CODECS_LOC_VARS
71        methodFull.Id, encoder, encoder2, true));
72
73    if (!encoder && !encoder2)
74      return E_FAIL;
75
76    CMyComPtr<IUnknown> encoderCommon = encoder ? (IUnknown *)encoder : (IUnknown *)encoder2;
77
78    #ifndef _7ZIP_ST
79    {
80      CMyComPtr<ICompressSetCoderMt> setCoderMt;
81      encoderCommon.QueryInterface(IID_ICompressSetCoderMt, &setCoderMt);
82      if (setCoderMt)
83      {
84        RINOK(setCoderMt->SetNumberOfThreads(_options.NumThreads));
85      }
86    }
87    #endif
88
89
90    RINOK(SetMethodProperties(methodFull, inSizeForReduce, encoderCommon));
91
92    /*
93    CMyComPtr<ICryptoResetSalt> resetSalt;
94    encoderCommon.QueryInterface(IID_ICryptoResetSalt, (void **)&resetSalt);
95    if (resetSalt != NULL)
96    {
97      resetSalt->ResetSalt();
98    }
99    */
100
101    #ifdef EXTERNAL_CODECS
102    CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
103    encoderCommon.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
104    if (setCompressCodecsInfo)
105    {
106      RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecsInfo));
107    }
108    #endif
109
110    CMyComPtr<ICryptoSetPassword> cryptoSetPassword;
111    encoderCommon.QueryInterface(IID_ICryptoSetPassword, &cryptoSetPassword);
112
113    if (cryptoSetPassword)
114    {
115      CByteBuffer buffer;
116      const UInt32 sizeInBytes = _options.Password.Length() * 2;
117      buffer.SetCapacity(sizeInBytes);
118      for (int i = 0; i < _options.Password.Length(); i++)
119      {
120        wchar_t c = _options.Password[i];
121        ((Byte *)buffer)[i * 2] = (Byte)c;
122        ((Byte *)buffer)[i * 2 + 1] = (Byte)(c >> 8);
123      }
124      RINOK(cryptoSetPassword->CryptoSetPassword((const Byte *)buffer, sizeInBytes));
125    }
126
127    if (encoder)
128      _mixerCoderSpec->AddCoder(encoder);
129    else
130      _mixerCoderSpec->AddCoder2(encoder2);
131  }
132  return S_OK;
133}
134
135HRESULT CEncoder::Encode(
136    DECL_EXTERNAL_CODECS_LOC_VARS
137    ISequentialInStream *inStream,
138    const UInt64 *inStreamSize, const UInt64 *inSizeForReduce,
139    CFolder &folderItem,
140    ISequentialOutStream *outStream,
141    CRecordVector<UInt64> &packSizes,
142    ICompressProgressInfo *compressProgress)
143{
144  RINOK(EncoderConstr());
145
146  if (_mixerCoderSpec == NULL)
147  {
148    RINOK(CreateMixerCoder(EXTERNAL_CODECS_LOC_VARS inSizeForReduce));
149  }
150  _mixerCoderSpec->ReInit();
151  // _mixerCoderSpec->SetCoderInfo(0, NULL, NULL, progress);
152
153  CObjectVector<CInOutTempBuffer> inOutTempBuffers;
154  CObjectVector<CSequentialOutTempBufferImp *> tempBufferSpecs;
155  CObjectVector<CMyComPtr<ISequentialOutStream> > tempBuffers;
156  int numMethods = _bindInfo.Coders.Size();
157  int i;
158  for (i = 1; i < _bindInfo.OutStreams.Size(); i++)
159  {
160    inOutTempBuffers.Add(CInOutTempBuffer());
161    inOutTempBuffers.Back().Create();
162    inOutTempBuffers.Back().InitWriting();
163  }
164  for (i = 1; i < _bindInfo.OutStreams.Size(); i++)
165  {
166    CSequentialOutTempBufferImp *tempBufferSpec = new CSequentialOutTempBufferImp;
167    CMyComPtr<ISequentialOutStream> tempBuffer = tempBufferSpec;
168    tempBufferSpec->Init(&inOutTempBuffers[i - 1]);
169    tempBuffers.Add(tempBuffer);
170    tempBufferSpecs.Add(tempBufferSpec);
171  }
172
173  for (i = 0; i < numMethods; i++)
174    _mixerCoderSpec->SetCoderInfo(i, NULL, NULL);
175
176  if (_bindInfo.InStreams.IsEmpty())
177    return E_FAIL;
178  UInt32 mainCoderIndex, mainStreamIndex;
179  _bindInfo.FindInStream(_bindInfo.InStreams[0], mainCoderIndex, mainStreamIndex);
180
181  if (inStreamSize != NULL)
182  {
183    CRecordVector<const UInt64 *> sizePointers;
184    for (UInt32 i = 0; i < _bindInfo.Coders[mainCoderIndex].NumInStreams; i++)
185      if (i == mainStreamIndex)
186        sizePointers.Add(inStreamSize);
187      else
188        sizePointers.Add(NULL);
189    _mixerCoderSpec->SetCoderInfo(mainCoderIndex, &sizePointers.Front(), NULL);
190  }
191
192
193  // UInt64 outStreamStartPos;
194  // RINOK(stream->Seek(0, STREAM_SEEK_CUR, &outStreamStartPos));
195
196  CSequentialInStreamSizeCount2 *inStreamSizeCountSpec = new CSequentialInStreamSizeCount2;
197  CMyComPtr<ISequentialInStream> inStreamSizeCount = inStreamSizeCountSpec;
198  CSequentialOutStreamSizeCount *outStreamSizeCountSpec = new CSequentialOutStreamSizeCount;
199  CMyComPtr<ISequentialOutStream> outStreamSizeCount = outStreamSizeCountSpec;
200
201  inStreamSizeCountSpec->Init(inStream);
202  outStreamSizeCountSpec->SetStream(outStream);
203  outStreamSizeCountSpec->Init();
204
205  CRecordVector<ISequentialInStream *> inStreamPointers;
206  CRecordVector<ISequentialOutStream *> outStreamPointers;
207  inStreamPointers.Add(inStreamSizeCount);
208  outStreamPointers.Add(outStreamSizeCount);
209  for (i = 1; i < _bindInfo.OutStreams.Size(); i++)
210    outStreamPointers.Add(tempBuffers[i - 1]);
211
212  for (i = 0; i < _codersInfo.Size(); i++)
213  {
214    CCoderInfo &encodingInfo = _codersInfo[i];
215
216    CMyComPtr<ICryptoResetInitVector> resetInitVector;
217    _mixerCoderSpec->_coders[i].QueryInterface(IID_ICryptoResetInitVector, (void **)&resetInitVector);
218    if (resetInitVector != NULL)
219    {
220      resetInitVector->ResetInitVector();
221    }
222
223    CMyComPtr<ICompressWriteCoderProperties> writeCoderProperties;
224    _mixerCoderSpec->_coders[i].QueryInterface(IID_ICompressWriteCoderProperties, (void **)&writeCoderProperties);
225    if (writeCoderProperties != NULL)
226    {
227      CDynBufSeqOutStream *outStreamSpec = new CDynBufSeqOutStream;
228      CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
229      outStreamSpec->Init();
230      writeCoderProperties->WriteCoderProperties(outStream);
231      outStreamSpec->CopyToBuffer(encodingInfo.Props);
232    }
233  }
234
235  UInt32 progressIndex = mainCoderIndex;
236
237  for (i = 0; i + 1 < _codersInfo.Size(); i++)
238  {
239    UInt64 m = _codersInfo[i].MethodID;
240    if (m == k_Delta || m == k_BCJ || m == k_BCJ2)
241      progressIndex = i + 1;
242  }
243
244  _mixerCoderSpec->SetProgressCoderIndex(progressIndex);
245
246  RINOK(_mixerCoder->Code(&inStreamPointers.Front(), NULL, 1,
247    &outStreamPointers.Front(), NULL, outStreamPointers.Size(), compressProgress));
248
249  ConvertBindInfoToFolderItemInfo(_decompressBindInfo, _decompressionMethods, folderItem);
250
251  packSizes.Add(outStreamSizeCountSpec->GetSize());
252
253  for (i = 1; i < _bindInfo.OutStreams.Size(); i++)
254  {
255    CInOutTempBuffer &inOutTempBuffer = inOutTempBuffers[i - 1];
256    RINOK(inOutTempBuffer.WriteToStream(outStream));
257    packSizes.Add(inOutTempBuffer.GetDataSize());
258  }
259
260  for (i = 0; i < (int)_bindReverseConverter->NumSrcInStreams; i++)
261  {
262    int binder = _bindInfo.FindBinderForInStream(
263        _bindReverseConverter->DestOutToSrcInMap[i]);
264    UInt64 streamSize;
265    if (binder < 0)
266      streamSize = inStreamSizeCountSpec->GetSize();
267    else
268      streamSize = _mixerCoderSpec->GetWriteProcessedSize(binder);
269    folderItem.UnpackSizes.Add(streamSize);
270  }
271  for (i = numMethods - 1; i >= 0; i--)
272    folderItem.Coders[numMethods - 1 - i].Props = _codersInfo[i].Props;
273  return S_OK;
274}
275
276
277CEncoder::CEncoder(const CCompressionMethodMode &options):
278  _bindReverseConverter(0),
279  _constructed(false)
280{
281  if (options.IsEmpty())
282    throw 1;
283
284  _options = options;
285  _mixerCoderSpec = NULL;
286}
287
288HRESULT CEncoder::EncoderConstr()
289{
290  if (_constructed)
291    return S_OK;
292  if (_options.Methods.IsEmpty())
293  {
294    // it has only password method;
295    if (!_options.PasswordIsDefined)
296      throw 1;
297    if (!_options.Binds.IsEmpty())
298      throw 1;
299    NCoderMixer::CCoderStreamsInfo coderStreamsInfo;
300    CMethodFull method;
301
302    method.NumInStreams = 1;
303    method.NumOutStreams = 1;
304    coderStreamsInfo.NumInStreams = 1;
305    coderStreamsInfo.NumOutStreams = 1;
306    method.Id = k_AES;
307
308    _options.Methods.Add(method);
309    _bindInfo.Coders.Add(coderStreamsInfo);
310
311    _bindInfo.InStreams.Add(0);
312    _bindInfo.OutStreams.Add(0);
313  }
314  else
315  {
316
317  UInt32 numInStreams = 0, numOutStreams = 0;
318  int i;
319  for (i = 0; i < _options.Methods.Size(); i++)
320  {
321    const CMethodFull &methodFull = _options.Methods[i];
322    NCoderMixer::CCoderStreamsInfo coderStreamsInfo;
323    coderStreamsInfo.NumInStreams = methodFull.NumOutStreams;
324    coderStreamsInfo.NumOutStreams = methodFull.NumInStreams;
325    if (_options.Binds.IsEmpty())
326    {
327      if (i < _options.Methods.Size() - 1)
328      {
329        NCoderMixer::CBindPair bindPair;
330        bindPair.InIndex = numInStreams + coderStreamsInfo.NumInStreams;
331        bindPair.OutIndex = numOutStreams;
332        _bindInfo.BindPairs.Add(bindPair);
333      }
334      else
335        _bindInfo.OutStreams.Insert(0, numOutStreams);
336      for (UInt32 j = 1; j < coderStreamsInfo.NumOutStreams; j++)
337        _bindInfo.OutStreams.Add(numOutStreams + j);
338    }
339
340    numInStreams += coderStreamsInfo.NumInStreams;
341    numOutStreams += coderStreamsInfo.NumOutStreams;
342
343    _bindInfo.Coders.Add(coderStreamsInfo);
344  }
345
346  if (!_options.Binds.IsEmpty())
347  {
348    for (i = 0; i < _options.Binds.Size(); i++)
349    {
350      NCoderMixer::CBindPair bindPair;
351      const CBind &bind = _options.Binds[i];
352      bindPair.InIndex = _bindInfo.GetCoderInStreamIndex(bind.InCoder) + bind.InStream;
353      bindPair.OutIndex = _bindInfo.GetCoderOutStreamIndex(bind.OutCoder) + bind.OutStream;
354      _bindInfo.BindPairs.Add(bindPair);
355    }
356    for (i = 0; i < (int)numOutStreams; i++)
357      if (_bindInfo.FindBinderForOutStream(i) == -1)
358        _bindInfo.OutStreams.Add(i);
359  }
360
361  for (i = 0; i < (int)numInStreams; i++)
362    if (_bindInfo.FindBinderForInStream(i) == -1)
363      _bindInfo.InStreams.Add(i);
364
365  if (_bindInfo.InStreams.IsEmpty())
366    throw 1; // this is error
367
368  // Make main stream first in list
369  int inIndex = _bindInfo.InStreams[0];
370  for (;;)
371  {
372    UInt32 coderIndex, coderStreamIndex;
373    _bindInfo.FindInStream(inIndex, coderIndex, coderStreamIndex);
374    UInt32 outIndex = _bindInfo.GetCoderOutStreamIndex(coderIndex);
375    int binder = _bindInfo.FindBinderForOutStream(outIndex);
376    if (binder >= 0)
377    {
378      inIndex = _bindInfo.BindPairs[binder].InIndex;
379      continue;
380    }
381    for (i = 0; i < _bindInfo.OutStreams.Size(); i++)
382      if (_bindInfo.OutStreams[i] == outIndex)
383      {
384        _bindInfo.OutStreams.Delete(i);
385        _bindInfo.OutStreams.Insert(0, outIndex);
386        break;
387      }
388    break;
389  }
390
391  if (_options.PasswordIsDefined)
392  {
393    int numCryptoStreams = _bindInfo.OutStreams.Size();
394
395    for (i = 0; i < numCryptoStreams; i++)
396    {
397      NCoderMixer::CBindPair bindPair;
398      bindPair.InIndex = numInStreams + i;
399      bindPair.OutIndex = _bindInfo.OutStreams[i];
400      _bindInfo.BindPairs.Add(bindPair);
401    }
402    _bindInfo.OutStreams.Clear();
403
404    /*
405    if (numCryptoStreams == 0)
406      numCryptoStreams = 1;
407    */
408
409    for (i = 0; i < numCryptoStreams; i++)
410    {
411      NCoderMixer::CCoderStreamsInfo coderStreamsInfo;
412      CMethodFull method;
413      method.NumInStreams = 1;
414      method.NumOutStreams = 1;
415      coderStreamsInfo.NumInStreams = method.NumOutStreams;
416      coderStreamsInfo.NumOutStreams = method.NumInStreams;
417      method.Id = k_AES;
418
419      _options.Methods.Add(method);
420      _bindInfo.Coders.Add(coderStreamsInfo);
421      _bindInfo.OutStreams.Add(numOutStreams + i);
422    }
423  }
424
425  }
426
427  for (int i = _options.Methods.Size() - 1; i >= 0; i--)
428  {
429    const CMethodFull &methodFull = _options.Methods[i];
430    _decompressionMethods.Add(methodFull.Id);
431  }
432
433  _bindReverseConverter = new NCoderMixer::CBindReverseConverter(_bindInfo);
434  _bindReverseConverter->CreateReverseBindInfo(_decompressBindInfo);
435  _constructed = true;
436  return S_OK;
437}
438
439CEncoder::~CEncoder()
440{
441  delete _bindReverseConverter;
442}
443
444}}
445