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