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
76HRESULT SetLzmaProp(PROPID propID, const PROPVARIANT &prop, CLzmaEncProps &ep)
77{
78  if (propID == NCoderPropID::kMatchFinder)
79  {
80    if (prop.vt != VT_BSTR)
81      return E_INVALIDARG;
82    return ParseMatchFinder(prop.bstrVal, &ep.btMode, &ep.numHashBytes) ? S_OK : E_INVALIDARG;
83  }
84  if (prop.vt != VT_UI4)
85    return E_INVALIDARG;
86  UInt32 v = prop.ulVal;
87  switch (propID)
88  {
89    case NCoderPropID::kNumFastBytes: ep.fb = v; break;
90    case NCoderPropID::kMatchFinderCycles: ep.mc = v; break;
91    case NCoderPropID::kAlgorithm: ep.algo = v; break;
92    case NCoderPropID::kDictionarySize: ep.dictSize = v; break;
93    case NCoderPropID::kPosStateBits: ep.pb = v; break;
94    case NCoderPropID::kLitPosBits: ep.lp = v; break;
95    case NCoderPropID::kLitContextBits: ep.lc = v; break;
96    default: return E_INVALIDARG;
97  }
98  return S_OK;
99}
100
101STDMETHODIMP CEncoder::SetCoderProperties(const PROPID *propIDs,
102    const PROPVARIANT *coderProps, UInt32 numProps)
103{
104  CLzmaEncProps props;
105  LzmaEncProps_Init(&props);
106
107  for (UInt32 i = 0; i < numProps; i++)
108  {
109    const PROPVARIANT &prop = coderProps[i];
110    PROPID propID = propIDs[i];
111    switch (propID)
112    {
113      case NCoderPropID::kEndMarker:
114        if (prop.vt != VT_BOOL) return E_INVALIDARG; props.writeEndMark = (prop.boolVal == VARIANT_TRUE); break;
115      case NCoderPropID::kNumThreads:
116        if (prop.vt != VT_UI4) return E_INVALIDARG; props.numThreads = prop.ulVal; break;
117      default:
118        RINOK(SetLzmaProp(propID, prop, props));
119    }
120  }
121  return SResToHRESULT(LzmaEnc_SetProps(_encoder, &props));
122}
123
124STDMETHODIMP CEncoder::WriteCoderProperties(ISequentialOutStream *outStream)
125{
126  Byte props[LZMA_PROPS_SIZE];
127  size_t size = LZMA_PROPS_SIZE;
128  RINOK(LzmaEnc_WriteProperties(_encoder, props, &size));
129  return WriteStream(outStream, props, size);
130}
131
132STDMETHODIMP CEncoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
133    const UInt64 * /* inSize */, const UInt64 * /* outSize */, ICompressProgressInfo *progress)
134{
135  CSeqInStreamWrap inWrap(inStream);
136  CSeqOutStreamWrap outWrap(outStream);
137  CCompressProgressWrap progressWrap(progress);
138
139  SRes res = LzmaEnc_Encode(_encoder, &outWrap.p, &inWrap.p, progress ? &progressWrap.p : NULL, &g_Alloc, &g_BigAlloc);
140  if (res == SZ_ERROR_READ && inWrap.Res != S_OK)
141    return inWrap.Res;
142  if (res == SZ_ERROR_WRITE && outWrap.Res != S_OK)
143    return outWrap.Res;
144  if (res == SZ_ERROR_PROGRESS && progressWrap.Res != S_OK)
145    return progressWrap.Res;
146  return SResToHRESULT(res);
147}
148
149}}
150