src/third-party/lzma/Lzma2Enc.c (view raw)
1/* Lzma2Enc.c -- LZMA2 Encoder
22012-06-19 : Igor Pavlov : Public domain */
3
4#include "Precomp.h"
5
6/* #include <stdio.h> */
7#include <string.h>
8
9/* #define _7ZIP_ST */
10
11#include "Lzma2Enc.h"
12
13#ifndef _7ZIP_ST
14#include "MtCoder.h"
15#else
16#define NUM_MT_CODER_THREADS_MAX 1
17#endif
18
19#define LZMA2_CONTROL_LZMA (1 << 7)
20#define LZMA2_CONTROL_COPY_NO_RESET 2
21#define LZMA2_CONTROL_COPY_RESET_DIC 1
22#define LZMA2_CONTROL_EOF 0
23
24#define LZMA2_LCLP_MAX 4
25
26#define LZMA2_DIC_SIZE_FROM_PROP(p) (((UInt32)2 | ((p) & 1)) << ((p) / 2 + 11))
27
28#define LZMA2_PACK_SIZE_MAX (1 << 16)
29#define LZMA2_COPY_CHUNK_SIZE LZMA2_PACK_SIZE_MAX
30#define LZMA2_UNPACK_SIZE_MAX (1 << 21)
31#define LZMA2_KEEP_WINDOW_SIZE LZMA2_UNPACK_SIZE_MAX
32
33#define LZMA2_CHUNK_SIZE_COMPRESSED_MAX ((1 << 16) + 16)
34
35
36#define PRF(x) /* x */
37
38/* ---------- CLzma2EncInt ---------- */
39
40typedef struct
41{
42 CLzmaEncHandle enc;
43 UInt64 srcPos;
44 Byte props;
45 Bool needInitState;
46 Bool needInitProp;
47} CLzma2EncInt;
48
49static SRes Lzma2EncInt_Init(CLzma2EncInt *p, const CLzma2EncProps *props)
50{
51 Byte propsEncoded[LZMA_PROPS_SIZE];
52 SizeT propsSize = LZMA_PROPS_SIZE;
53 RINOK(LzmaEnc_SetProps(p->enc, &props->lzmaProps));
54 RINOK(LzmaEnc_WriteProperties(p->enc, propsEncoded, &propsSize));
55 p->srcPos = 0;
56 p->props = propsEncoded[0];
57 p->needInitState = True;
58 p->needInitProp = True;
59 return SZ_OK;
60}
61
62SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp, ISeqInStream *inStream, UInt32 keepWindowSize,
63 ISzAlloc *alloc, ISzAlloc *allocBig);
64SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen,
65 UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig);
66SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit,
67 Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize);
68const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp);
69void LzmaEnc_Finish(CLzmaEncHandle pp);
70void LzmaEnc_SaveState(CLzmaEncHandle pp);
71void LzmaEnc_RestoreState(CLzmaEncHandle pp);
72
73
74static SRes Lzma2EncInt_EncodeSubblock(CLzma2EncInt *p, Byte *outBuf,
75 size_t *packSizeRes, ISeqOutStream *outStream)
76{
77 size_t packSizeLimit = *packSizeRes;
78 size_t packSize = packSizeLimit;
79 UInt32 unpackSize = LZMA2_UNPACK_SIZE_MAX;
80 unsigned lzHeaderSize = 5 + (p->needInitProp ? 1 : 0);
81 Bool useCopyBlock;
82 SRes res;
83
84 *packSizeRes = 0;
85 if (packSize < lzHeaderSize)
86 return SZ_ERROR_OUTPUT_EOF;
87 packSize -= lzHeaderSize;
88
89 LzmaEnc_SaveState(p->enc);
90 res = LzmaEnc_CodeOneMemBlock(p->enc, p->needInitState,
91 outBuf + lzHeaderSize, &packSize, LZMA2_PACK_SIZE_MAX, &unpackSize);
92
93 PRF(printf("\npackSize = %7d unpackSize = %7d ", packSize, unpackSize));
94
95 if (unpackSize == 0)
96 return res;
97
98 if (res == SZ_OK)
99 useCopyBlock = (packSize + 2 >= unpackSize || packSize > (1 << 16));
100 else
101 {
102 if (res != SZ_ERROR_OUTPUT_EOF)
103 return res;
104 res = SZ_OK;
105 useCopyBlock = True;
106 }
107
108 if (useCopyBlock)
109 {
110 size_t destPos = 0;
111 PRF(printf("################# COPY "));
112 while (unpackSize > 0)
113 {
114 UInt32 u = (unpackSize < LZMA2_COPY_CHUNK_SIZE) ? unpackSize : LZMA2_COPY_CHUNK_SIZE;
115 if (packSizeLimit - destPos < u + 3)
116 return SZ_ERROR_OUTPUT_EOF;
117 outBuf[destPos++] = (Byte)(p->srcPos == 0 ? LZMA2_CONTROL_COPY_RESET_DIC : LZMA2_CONTROL_COPY_NO_RESET);
118 outBuf[destPos++] = (Byte)((u - 1) >> 8);
119 outBuf[destPos++] = (Byte)(u - 1);
120 memcpy(outBuf + destPos, LzmaEnc_GetCurBuf(p->enc) - unpackSize, u);
121 unpackSize -= u;
122 destPos += u;
123 p->srcPos += u;
124 if (outStream)
125 {
126 *packSizeRes += destPos;
127 if (outStream->Write(outStream, outBuf, destPos) != destPos)
128 return SZ_ERROR_WRITE;
129 destPos = 0;
130 }
131 else
132 *packSizeRes = destPos;
133 /* needInitState = True; */
134 }
135 LzmaEnc_RestoreState(p->enc);
136 return SZ_OK;
137 }
138 {
139 size_t destPos = 0;
140 UInt32 u = unpackSize - 1;
141 UInt32 pm = (UInt32)(packSize - 1);
142 unsigned mode = (p->srcPos == 0) ? 3 : (p->needInitState ? (p->needInitProp ? 2 : 1) : 0);
143
144 PRF(printf(" "));
145
146 outBuf[destPos++] = (Byte)(LZMA2_CONTROL_LZMA | (mode << 5) | ((u >> 16) & 0x1F));
147 outBuf[destPos++] = (Byte)(u >> 8);
148 outBuf[destPos++] = (Byte)u;
149 outBuf[destPos++] = (Byte)(pm >> 8);
150 outBuf[destPos++] = (Byte)pm;
151
152 if (p->needInitProp)
153 outBuf[destPos++] = p->props;
154
155 p->needInitProp = False;
156 p->needInitState = False;
157 destPos += packSize;
158 p->srcPos += unpackSize;
159
160 if (outStream)
161 if (outStream->Write(outStream, outBuf, destPos) != destPos)
162 return SZ_ERROR_WRITE;
163 *packSizeRes = destPos;
164 return SZ_OK;
165 }
166}
167
168/* ---------- Lzma2 Props ---------- */
169
170void Lzma2EncProps_Init(CLzma2EncProps *p)
171{
172 LzmaEncProps_Init(&p->lzmaProps);
173 p->numTotalThreads = -1;
174 p->numBlockThreads = -1;
175 p->blockSize = 0;
176}
177
178void Lzma2EncProps_Normalize(CLzma2EncProps *p)
179{
180 int t1, t1n, t2, t3;
181 {
182 CLzmaEncProps lzmaProps = p->lzmaProps;
183 LzmaEncProps_Normalize(&lzmaProps);
184 t1n = lzmaProps.numThreads;
185 }
186
187 t1 = p->lzmaProps.numThreads;
188 t2 = p->numBlockThreads;
189 t3 = p->numTotalThreads;
190
191 if (t2 > NUM_MT_CODER_THREADS_MAX)
192 t2 = NUM_MT_CODER_THREADS_MAX;
193
194 if (t3 <= 0)
195 {
196 if (t2 <= 0)
197 t2 = 1;
198 t3 = t1n * t2;
199 }
200 else if (t2 <= 0)
201 {
202 t2 = t3 / t1n;
203 if (t2 == 0)
204 {
205 t1 = 1;
206 t2 = t3;
207 }
208 if (t2 > NUM_MT_CODER_THREADS_MAX)
209 t2 = NUM_MT_CODER_THREADS_MAX;
210 }
211 else if (t1 <= 0)
212 {
213 t1 = t3 / t2;
214 if (t1 == 0)
215 t1 = 1;
216 }
217 else
218 t3 = t1n * t2;
219
220 p->lzmaProps.numThreads = t1;
221
222 LzmaEncProps_Normalize(&p->lzmaProps);
223
224 if (p->blockSize == 0)
225 {
226 UInt32 dictSize = p->lzmaProps.dictSize;
227 UInt64 blockSize = (UInt64)dictSize << 2;
228 const UInt32 kMinSize = (UInt32)1 << 20;
229 const UInt32 kMaxSize = (UInt32)1 << 28;
230 if (blockSize < kMinSize) blockSize = kMinSize;
231 if (blockSize > kMaxSize) blockSize = kMaxSize;
232 if (blockSize < dictSize) blockSize = dictSize;
233 p->blockSize = (size_t)blockSize;
234 }
235 if (t2 > 1)
236 {
237 UInt64 temp = p->lzmaProps.reduceSize + p->blockSize - 1;
238 if (temp > p->lzmaProps.reduceSize)
239 {
240 UInt64 numBlocks = temp / p->blockSize;
241 if (numBlocks < t2)
242 {
243 t2 = (UInt32)numBlocks;
244 t3 = t1 * t2;
245 }
246 }
247 }
248 p->numBlockThreads = t2;
249 p->numTotalThreads = t3;
250}
251
252static SRes Progress(ICompressProgress *p, UInt64 inSize, UInt64 outSize)
253{
254 return (p && p->Progress(p, inSize, outSize) != SZ_OK) ? SZ_ERROR_PROGRESS : SZ_OK;
255}
256
257/* ---------- Lzma2 ---------- */
258
259typedef struct
260{
261 Byte propEncoded;
262 CLzma2EncProps props;
263
264 Byte *outBuf;
265
266 ISzAlloc *alloc;
267 ISzAlloc *allocBig;
268
269 CLzma2EncInt coders[NUM_MT_CODER_THREADS_MAX];
270
271 #ifndef _7ZIP_ST
272 CMtCoder mtCoder;
273 #endif
274
275} CLzma2Enc;
276
277
278/* ---------- Lzma2EncThread ---------- */
279
280static SRes Lzma2Enc_EncodeMt1(CLzma2EncInt *p, CLzma2Enc *mainEncoder,
281 ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress)
282{
283 UInt64 packTotal = 0;
284 SRes res = SZ_OK;
285
286 if (mainEncoder->outBuf == 0)
287 {
288 mainEncoder->outBuf = (Byte *)IAlloc_Alloc(mainEncoder->alloc, LZMA2_CHUNK_SIZE_COMPRESSED_MAX);
289 if (mainEncoder->outBuf == 0)
290 return SZ_ERROR_MEM;
291 }
292 RINOK(Lzma2EncInt_Init(p, &mainEncoder->props));
293 RINOK(LzmaEnc_PrepareForLzma2(p->enc, inStream, LZMA2_KEEP_WINDOW_SIZE,
294 mainEncoder->alloc, mainEncoder->allocBig));
295 for (;;)
296 {
297 size_t packSize = LZMA2_CHUNK_SIZE_COMPRESSED_MAX;
298 res = Lzma2EncInt_EncodeSubblock(p, mainEncoder->outBuf, &packSize, outStream);
299 if (res != SZ_OK)
300 break;
301 packTotal += packSize;
302 res = Progress(progress, p->srcPos, packTotal);
303 if (res != SZ_OK)
304 break;
305 if (packSize == 0)
306 break;
307 }
308 LzmaEnc_Finish(p->enc);
309 if (res == SZ_OK)
310 {
311 Byte b = 0;
312 if (outStream->Write(outStream, &b, 1) != 1)
313 return SZ_ERROR_WRITE;
314 }
315 return res;
316}
317
318#ifndef _7ZIP_ST
319
320typedef struct
321{
322 IMtCoderCallback funcTable;
323 CLzma2Enc *lzma2Enc;
324} CMtCallbackImp;
325
326static SRes MtCallbackImp_Code(void *pp, unsigned index, Byte *dest, size_t *destSize,
327 const Byte *src, size_t srcSize, int finished)
328{
329 CMtCallbackImp *imp = (CMtCallbackImp *)pp;
330 CLzma2Enc *mainEncoder = imp->lzma2Enc;
331 CLzma2EncInt *p = &mainEncoder->coders[index];
332
333 SRes res = SZ_OK;
334 {
335 size_t destLim = *destSize;
336 *destSize = 0;
337
338 if (srcSize != 0)
339 {
340 RINOK(Lzma2EncInt_Init(p, &mainEncoder->props));
341
342 RINOK(LzmaEnc_MemPrepare(p->enc, src, srcSize, LZMA2_KEEP_WINDOW_SIZE,
343 mainEncoder->alloc, mainEncoder->allocBig));
344
345 while (p->srcPos < srcSize)
346 {
347 size_t packSize = destLim - *destSize;
348 res = Lzma2EncInt_EncodeSubblock(p, dest + *destSize, &packSize, NULL);
349 if (res != SZ_OK)
350 break;
351 *destSize += packSize;
352
353 if (packSize == 0)
354 {
355 res = SZ_ERROR_FAIL;
356 break;
357 }
358
359 if (MtProgress_Set(&mainEncoder->mtCoder.mtProgress, index, p->srcPos, *destSize) != SZ_OK)
360 {
361 res = SZ_ERROR_PROGRESS;
362 break;
363 }
364 }
365 LzmaEnc_Finish(p->enc);
366 if (res != SZ_OK)
367 return res;
368 }
369 if (finished)
370 {
371 if (*destSize == destLim)
372 return SZ_ERROR_OUTPUT_EOF;
373 dest[(*destSize)++] = 0;
374 }
375 }
376 return res;
377}
378
379#endif
380
381/* ---------- Lzma2Enc ---------- */
382
383CLzma2EncHandle Lzma2Enc_Create(ISzAlloc *alloc, ISzAlloc *allocBig)
384{
385 CLzma2Enc *p = (CLzma2Enc *)alloc->Alloc(alloc, sizeof(CLzma2Enc));
386 if (p == 0)
387 return NULL;
388 Lzma2EncProps_Init(&p->props);
389 Lzma2EncProps_Normalize(&p->props);
390 p->outBuf = 0;
391 p->alloc = alloc;
392 p->allocBig = allocBig;
393 {
394 unsigned i;
395 for (i = 0; i < NUM_MT_CODER_THREADS_MAX; i++)
396 p->coders[i].enc = 0;
397 }
398 #ifndef _7ZIP_ST
399 MtCoder_Construct(&p->mtCoder);
400 #endif
401
402 return p;
403}
404
405void Lzma2Enc_Destroy(CLzma2EncHandle pp)
406{
407 CLzma2Enc *p = (CLzma2Enc *)pp;
408 unsigned i;
409 for (i = 0; i < NUM_MT_CODER_THREADS_MAX; i++)
410 {
411 CLzma2EncInt *t = &p->coders[i];
412 if (t->enc)
413 {
414 LzmaEnc_Destroy(t->enc, p->alloc, p->allocBig);
415 t->enc = 0;
416 }
417 }
418
419 #ifndef _7ZIP_ST
420 MtCoder_Destruct(&p->mtCoder);
421 #endif
422
423 IAlloc_Free(p->alloc, p->outBuf);
424 IAlloc_Free(p->alloc, pp);
425}
426
427SRes Lzma2Enc_SetProps(CLzma2EncHandle pp, const CLzma2EncProps *props)
428{
429 CLzma2Enc *p = (CLzma2Enc *)pp;
430 CLzmaEncProps lzmaProps = props->lzmaProps;
431 LzmaEncProps_Normalize(&lzmaProps);
432 if (lzmaProps.lc + lzmaProps.lp > LZMA2_LCLP_MAX)
433 return SZ_ERROR_PARAM;
434 p->props = *props;
435 Lzma2EncProps_Normalize(&p->props);
436 return SZ_OK;
437}
438
439Byte Lzma2Enc_WriteProperties(CLzma2EncHandle pp)
440{
441 CLzma2Enc *p = (CLzma2Enc *)pp;
442 unsigned i;
443 UInt32 dicSize = LzmaEncProps_GetDictSize(&p->props.lzmaProps);
444 for (i = 0; i < 40; i++)
445 if (dicSize <= LZMA2_DIC_SIZE_FROM_PROP(i))
446 break;
447 return (Byte)i;
448}
449
450SRes Lzma2Enc_Encode(CLzma2EncHandle pp,
451 ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress)
452{
453 CLzma2Enc *p = (CLzma2Enc *)pp;
454 int i;
455
456 for (i = 0; i < p->props.numBlockThreads; i++)
457 {
458 CLzma2EncInt *t = &p->coders[i];
459 if (t->enc == NULL)
460 {
461 t->enc = LzmaEnc_Create(p->alloc);
462 if (t->enc == NULL)
463 return SZ_ERROR_MEM;
464 }
465 }
466
467 #ifndef _7ZIP_ST
468 if (p->props.numBlockThreads <= 1)
469 #endif
470 return Lzma2Enc_EncodeMt1(&p->coders[0], p, outStream, inStream, progress);
471
472 #ifndef _7ZIP_ST
473
474 {
475 CMtCallbackImp mtCallback;
476
477 mtCallback.funcTable.Code = MtCallbackImp_Code;
478 mtCallback.lzma2Enc = p;
479
480 p->mtCoder.progress = progress;
481 p->mtCoder.inStream = inStream;
482 p->mtCoder.outStream = outStream;
483 p->mtCoder.alloc = p->alloc;
484 p->mtCoder.mtCallback = &mtCallback.funcTable;
485
486 p->mtCoder.blockSize = p->props.blockSize;
487 p->mtCoder.destBlockSize = p->props.blockSize + (p->props.blockSize >> 10) + 16;
488 p->mtCoder.numThreads = p->props.numBlockThreads;
489
490 return MtCoder_Code(&p->mtCoder);
491 }
492 #endif
493}