1// LzmaRam.cpp
2
3#include "StdAfx.h"
4#include "../../../Common/Types.h"
5#include "../LZMA/LZMADecoder.h"
6#include "../LZMA/LZMAEncoder.h"
7#include "LzmaRam.h"
8
9extern "C"
10{
11  #include "../../../../C/Compress/Branch/BranchX86.h"
12}
13
14class CInStreamRam:
15  public ISequentialInStream,
16  public CMyUnknownImp
17{
18  const Byte *Data;
19  size_t Size;
20  size_t Pos;
21public:
22  MY_UNKNOWN_IMP
23  void Init(const Byte *data, size_t size)
24  {
25    Data = data;
26    Size = size;
27    Pos = 0;
28  }
29  STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
30};
31
32STDMETHODIMP CInStreamRam::Read(void *data, UInt32 size, UInt32 *processedSize)
33{
34  if (size > (Size - Pos))
35    size = (UInt32)(Size - Pos);
36  for (UInt32 i = 0; i < size; i++)
37    ((Byte *)data)[i] = Data[Pos + i];
38  Pos += size;
39  if(processedSize != NULL)
40    *processedSize = size;
41  return S_OK;
42}
43
44class COutStreamRam:
45  public ISequentialOutStream,
46  public CMyUnknownImp
47{
48  size_t Size;
49public:
50  Byte *Data;
51  size_t Pos;
52  bool Overflow;
53  void Init(Byte *data, size_t size)
54  {
55    Data = data;
56    Size = size;
57    Pos = 0;
58    Overflow = false;
59  }
60  void SetPos(size_t pos)
61  {
62    Overflow = false;
63    Pos = pos;
64  }
65  MY_UNKNOWN_IMP
66  HRESULT WriteByte(Byte b)
67  {
68    if (Pos >= Size)
69    {
70      Overflow = true;
71      return E_FAIL;
72    }
73    Data[Pos++] = b;
74    return S_OK;
75  }
76  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
77};
78
79STDMETHODIMP COutStreamRam::Write(const void *data, UInt32 size, UInt32 *processedSize)
80{
81  UInt32 i;
82  for (i = 0; i < size && Pos < Size; i++)
83    Data[Pos++] = ((const Byte *)data)[i];
84  if(processedSize != NULL)
85    *processedSize = i;
86  if (i != size)
87  {
88    Overflow = true;
89    return E_FAIL;
90  }
91  return S_OK;
92}
93
94#define SZ_RAM_E_FAIL (1)
95#define SZ_RAM_E_OUTOFMEMORY (2)
96#define SZE_OUT_OVERFLOW (3)
97
98int LzmaRamEncode(
99    const Byte *inBuffer, size_t inSize,
100    Byte *outBuffer, size_t outSize, size_t *outSizeProcessed,
101    UInt32 dictionarySize, ESzFilterMode filterMode)
102{
103  #ifndef _NO_EXCEPTIONS
104  try {
105  #endif
106
107  *outSizeProcessed = 0;
108  const size_t kIdSize = 1;
109  const size_t kLzmaPropsSize = 5;
110  const size_t kMinDestSize = kIdSize + kLzmaPropsSize + 8;
111  if (outSize < kMinDestSize)
112    return SZE_OUT_OVERFLOW;
113  NCompress::NLZMA::CEncoder *encoderSpec = new NCompress::NLZMA::CEncoder;
114  CMyComPtr<ICompressCoder> encoder = encoderSpec;
115
116  PROPID propIDs[] =
117  {
118    NCoderPropID::kAlgorithm,
119    NCoderPropID::kDictionarySize,
120    NCoderPropID::kNumFastBytes,
121  };
122  const int kNumProps = sizeof(propIDs) / sizeof(propIDs[0]);
123  PROPVARIANT properties[kNumProps];
124  properties[0].vt = VT_UI4;
125  properties[1].vt = VT_UI4;
126  properties[2].vt = VT_UI4;
127  properties[0].ulVal = (UInt32)2;
128  properties[1].ulVal = (UInt32)dictionarySize;
129  properties[2].ulVal = (UInt32)64;
130
131  if (encoderSpec->SetCoderProperties(propIDs, properties, kNumProps) != S_OK)
132    return 1;
133
134  COutStreamRam *outStreamSpec = new COutStreamRam;
135  if (outStreamSpec == 0)
136    return SZ_RAM_E_OUTOFMEMORY;
137  CMyComPtr<ISequentialOutStream> outStream = outStreamSpec;
138  CInStreamRam *inStreamSpec = new CInStreamRam;
139  if (inStreamSpec == 0)
140    return SZ_RAM_E_OUTOFMEMORY;
141  CMyComPtr<ISequentialInStream> inStream = inStreamSpec;
142
143  outStreamSpec->Init(outBuffer, outSize);
144  if (outStreamSpec->WriteByte(0) != S_OK)
145    return SZE_OUT_OVERFLOW;
146
147  if (encoderSpec->WriteCoderProperties(outStream) != S_OK)
148    return SZE_OUT_OVERFLOW;
149  if (outStreamSpec->Pos != kIdSize + kLzmaPropsSize)
150    return 1;
151
152  int i;
153  for (i = 0; i < 8; i++)
154  {
155    UInt64 t = (UInt64)(inSize);
156    if (outStreamSpec->WriteByte((Byte)((t) >> (8 * i))) != S_OK)
157      return SZE_OUT_OVERFLOW;
158  }
159
160  Byte *filteredStream = 0;
161
162  bool useFilter = (filterMode != SZ_FILTER_NO);
163  if (useFilter)
164  {
165    if (inSize != 0)
166    {
167      filteredStream = (Byte *)MyAlloc(inSize);
168      if (filteredStream == 0)
169        return SZ_RAM_E_OUTOFMEMORY;
170      memmove(filteredStream, inBuffer, inSize);
171    }
172    UInt32 x86State;
173    x86_Convert_Init(x86State);
174    x86_Convert(filteredStream, (SizeT)inSize, 0, &x86State, 1);
175  }
176
177  size_t minSize = 0;
178  int numPasses = (filterMode == SZ_FILTER_AUTO) ? 3 : 1;
179  bool bestIsFiltered = false;
180  int mainResult = 0;
181  size_t startPos = outStreamSpec->Pos;
182  for (i = 0; i < numPasses; i++)
183  {
184    if (numPasses > 1 && i == numPasses - 1 && !bestIsFiltered)
185      break;
186    outStreamSpec->SetPos(startPos);
187    bool curModeIsFiltered = false;
188    if (useFilter && i == 0)
189      curModeIsFiltered = true;
190    if (numPasses > 1 && i == numPasses - 1)
191      curModeIsFiltered = true;
192
193    inStreamSpec->Init(curModeIsFiltered ? filteredStream : inBuffer, inSize);
194
195    HRESULT lzmaResult = encoder->Code(inStream, outStream, 0, 0, 0);
196
197    mainResult = 0;
198    if (lzmaResult == E_OUTOFMEMORY)
199    {
200      mainResult = SZ_RAM_E_OUTOFMEMORY;
201      break;
202    }
203    if (i == 0 || outStreamSpec->Pos <= minSize)
204    {
205      minSize = outStreamSpec->Pos;
206      bestIsFiltered = curModeIsFiltered;
207    }
208    if (outStreamSpec->Overflow)
209      mainResult = SZE_OUT_OVERFLOW;
210    else if (lzmaResult != S_OK)
211    {
212      mainResult = SZ_RAM_E_FAIL;
213      break;
214    }
215  }
216  *outSizeProcessed = outStreamSpec->Pos;
217  if (bestIsFiltered)
218    outBuffer[0] = 1;
219  if (useFilter)
220    MyFree(filteredStream);
221  return mainResult;
222
223  #ifndef _NO_EXCEPTIONS
224  } catch(...) { return SZ_RAM_E_OUTOFMEMORY; }
225  #endif
226}
227