1// LzmaEncoder.cpp
2
3#include "StdAfx.h"
4
5#include "../../../C/Alloc.h"
6
7#include "../Common/CWrappers.h"
8#include "../Common/StreamUtils.h"
9
10#include "LzmaEncoder.h"
11
12namespace NCompress {
13namespace NLzma {
14
15static void *SzBigAlloc(void *, size_t size) { return BigAlloc(size); }
16static void SzBigFree(void *, void *address) { BigFree(address); }
17static ISzAlloc g_BigAlloc = { SzBigAlloc, SzBigFree };
18
19static void *SzAlloc(void *, size_t size) { return MyAlloc(size); }
20static void SzFree(void *, void *address) { MyFree(address); }
21static ISzAlloc g_Alloc = { SzAlloc, SzFree };
22
23CEncoder::CEncoder()
24{
25  _encoder = 0;
26  _encoder = LzmaEnc_Create(&g_Alloc);
27  if (_encoder == 0)
28    throw 1;
29}
30
31CEncoder::~CEncoder()
32{
33  if (_encoder != 0)
34    LzmaEnc_Destroy(_encoder, &g_Alloc, &g_BigAlloc);
35}
36
37inline wchar_t GetUpperChar(wchar_t c)
38{
39  if (c >= 'a' && c <= 'z')
40    c -= 0x20;
41  return c;
42}
43
44static int ParseMatchFinder(const wchar_t *s, int *btMode, int *numHashBytes)
45{
46  wchar_t c = GetUpperChar(*s++);
47  if (c == L'H')
48  {
49    if (GetUpperChar(*s++) != L'C')
50      return 0;
51    int numHashBytesLoc = (int)(*s++ - L'0');
52    if (numHashBytesLoc < 4 || numHashBytesLoc > 4)
53      return 0;
54    if (*s++ != 0)
55      return 0;
56    *btMode = 0;
57    *numHashBytes = numHashBytesLoc;
58    return 1;
59  }
60  if (c != L'B')
61    return 0;
62
63  if (GetUpperChar(*s++) != L'T')
64    return 0;
65  int numHashBytesLoc = (int)(*s++ - L'0');
66  if (numHashBytesLoc < 2 || numHashBytesLoc > 4)
67    return 0;
68  c = GetUpperChar(*s++);
69  if (c != L'\0')
70    return 0;
71  *btMode = 1;
72  *numHashBytes = numHashBytesLoc;
73  return 1;
74}
75
76#define SET_PROP_32(_id_, _dest_) case NCoderPropID::_id_: ep._dest_ = v; break;
77
78HRESULT SetLzmaProp(PROPID propID, const PROPVARIANT &prop, CLzmaEncProps &ep)
79{
80  if (propID == NCoderPropID::kMatchFinder)
81  {
82    if (prop.vt != VT_BSTR)
83      return E_INVALIDARG;
84    return ParseMatchFinder(prop.bstrVal, &ep.btMode, &ep.numHashBytes) ? S_OK : E_INVALIDARG;
85  }
86  if (propID > NCoderPropID::kReduceSize)
87    return S_OK;
88  if (propID == NCoderPropID::kReduceSize)
89  {
90    if (prop.vt == VT_UI8)
91      ep.reduceSize = prop.uhVal.QuadPart;
92    return S_OK;
93  }
94  if (prop.vt != VT_UI4)
95    return E_INVALIDARG;
96  UInt32 v = prop.ulVal;
97  switch (propID)
98  {
99    case NCoderPropID::kDefaultProp: if (v > 31) return E_INVALIDARG; ep.dictSize = (UInt32)1 << (unsigned)v; break;
100    SET_PROP_32(kLevel, level)
101    SET_PROP_32(kNumFastBytes, fb)
102    SET_PROP_32(kMatchFinderCycles, mc)
103    SET_PROP_32(kAlgorithm, algo)
104    SET_PROP_32(kDictionarySize, dictSize)
105    SET_PROP_32(kPosStateBits, pb)
106    SET_PROP_32(kLitPosBits, lp)
107    SET_PROP_32(kLitContextBits, lc)
108    SET_PROP_32(kNumThreads, numThreads)
109    default: return E_INVALIDARG;
110  }
111  return S_OK;
112}
113
114STDMETHODIMP CEncoder::SetCoderProperties(const PROPID *propIDs,
115    const PROPVARIANT *coderProps, UInt32 numProps)
116{
117  CLzmaEncProps props;
118  LzmaEncProps_Init(&props);
119
120  for (UInt32 i = 0; i < numProps; i++)
121  {
122    const PROPVARIANT &prop = coderProps[i];
123    PROPID propID = propIDs[i];
124    switch (propID)
125    {
126      case NCoderPropID::kEndMarker:
127        if (prop.vt != VT_BOOL) return E_INVALIDARG; props.writeEndMark = (prop.boolVal != VARIANT_FALSE); break;
128      default:
129        RINOK(SetLzmaProp(propID, prop, props));
130    }
131  }
132  return SResToHRESULT(LzmaEnc_SetProps(_encoder, &props));
133}
134
135STDMETHODIMP CEncoder::WriteCoderProperties(ISequentialOutStream *outStream)
136{
137  Byte props[LZMA_PROPS_SIZE];
138  size_t size = LZMA_PROPS_SIZE;
139  RINOK(LzmaEnc_WriteProperties(_encoder, props, &size));
140  return WriteStream(outStream, props, size);
141}
142
143STDMETHODIMP CEncoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
144    const UInt64 * /* inSize */, const UInt64 * /* outSize */, ICompressProgressInfo *progress)
145{
146  CSeqInStreamWrap inWrap(inStream);
147  CSeqOutStreamWrap outWrap(outStream);
148  CCompressProgressWrap progressWrap(progress);
149
150  SRes res = LzmaEnc_Encode(_encoder, &outWrap.p, &inWrap.p, progress ? &progressWrap.p : NULL, &g_Alloc, &g_BigAlloc);
151  _inputProcessed = inWrap.Processed;
152  if (res == SZ_ERROR_READ && inWrap.Res != S_OK)
153    return inWrap.Res;
154  if (res == SZ_ERROR_WRITE && outWrap.Res != S_OK)
155    return outWrap.Res;
156  if (res == SZ_ERROR_PROGRESS && progressWrap.Res != S_OK)
157    return progressWrap.Res;
158  return SResToHRESULT(res);
159}
160
161}}
162