1 /* Lzma2Enc.c -- LZMA2 Encoder
2 2018-07-04 : Igor Pavlov : Public domain */
15 #define MTCODER__THREADS_MAX 1
18 #define LZMA2_CONTROL_LZMA (1 << 7)
19 #define LZMA2_CONTROL_COPY_NO_RESET 2
20 #define LZMA2_CONTROL_COPY_RESET_DIC 1
21 #define LZMA2_CONTROL_EOF 0
23 #define LZMA2_LCLP_MAX 4
25 #define LZMA2_DIC_SIZE_FROM_PROP(p) (((UInt32)2 | ((p) & 1)) << ((p) / 2 + 11))
27 #define LZMA2_PACK_SIZE_MAX (1 << 16)
28 #define LZMA2_COPY_CHUNK_SIZE LZMA2_PACK_SIZE_MAX
29 #define LZMA2_UNPACK_SIZE_MAX (1 << 21)
30 #define LZMA2_KEEP_WINDOW_SIZE LZMA2_UNPACK_SIZE_MAX
32 #define LZMA2_CHUNK_SIZE_COMPRESSED_MAX ((1 << 16) + 16)
35 #define PRF(x) /* x */
38 /* ---------- CLimitedSeqInStream ---------- */
43 ISeqInStream
*realStream
;
47 } CLimitedSeqInStream
;
49 static void LimitedSeqInStream_Init(CLimitedSeqInStream
*p
)
51 p
->limit
= (UInt64
)(Int64
)-1;
56 static SRes
LimitedSeqInStream_Read(const ISeqInStream
*pp
, void *data
, size_t *size
)
58 CLimitedSeqInStream
*p
= CONTAINER_FROM_VTBL(pp
, CLimitedSeqInStream
, vt
);
62 if (p
->limit
!= (UInt64
)(Int64
)-1)
64 UInt64 rem
= p
->limit
- p
->processed
;
70 res
= ISeqInStream_Read(p
->realStream
, data
, &size2
);
71 p
->finished
= (size2
== 0 ? 1 : 0);
72 p
->processed
+= size2
;
79 /* ---------- CLzma2EncInt ---------- */
92 static SRes
Lzma2EncInt_InitStream(CLzma2EncInt
*p
, const CLzma2EncProps
*props
)
96 SizeT propsSize
= LZMA_PROPS_SIZE
;
97 Byte propsEncoded
[LZMA_PROPS_SIZE
];
98 RINOK(LzmaEnc_SetProps(p
->enc
, &props
->lzmaProps
));
99 RINOK(LzmaEnc_WriteProperties(p
->enc
, propsEncoded
, &propsSize
));
100 p
->propsByte
= propsEncoded
[0];
101 p
->propsAreSet
= True
;
106 static void Lzma2EncInt_InitBlock(CLzma2EncInt
*p
)
109 p
->needInitState
= True
;
110 p
->needInitProp
= True
;
114 SRes
LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp
, ISeqInStream
*inStream
, UInt32 keepWindowSize
,
115 ISzAllocPtr alloc
, ISzAllocPtr allocBig
);
116 SRes
LzmaEnc_MemPrepare(CLzmaEncHandle pp
, const Byte
*src
, SizeT srcLen
,
117 UInt32 keepWindowSize
, ISzAllocPtr alloc
, ISzAllocPtr allocBig
);
118 SRes
LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp
, BoolInt reInit
,
119 Byte
*dest
, size_t *destLen
, UInt32 desiredPackSize
, UInt32
*unpackSize
);
120 const Byte
*LzmaEnc_GetCurBuf(CLzmaEncHandle pp
);
121 void LzmaEnc_Finish(CLzmaEncHandle pp
);
122 void LzmaEnc_SaveState(CLzmaEncHandle pp
);
123 void LzmaEnc_RestoreState(CLzmaEncHandle pp
);
126 UInt32 LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp);
129 static SRes
Lzma2EncInt_EncodeSubblock(CLzma2EncInt
*p
, Byte
*outBuf
,
130 size_t *packSizeRes
, ISeqOutStream
*outStream
)
132 size_t packSizeLimit
= *packSizeRes
;
133 size_t packSize
= packSizeLimit
;
134 UInt32 unpackSize
= LZMA2_UNPACK_SIZE_MAX
;
135 unsigned lzHeaderSize
= 5 + (p
->needInitProp
? 1 : 0);
136 BoolInt useCopyBlock
;
140 if (packSize
< lzHeaderSize
)
141 return SZ_ERROR_OUTPUT_EOF
;
142 packSize
-= lzHeaderSize
;
144 LzmaEnc_SaveState(p
->enc
);
145 res
= LzmaEnc_CodeOneMemBlock(p
->enc
, p
->needInitState
,
146 outBuf
+ lzHeaderSize
, &packSize
, LZMA2_PACK_SIZE_MAX
, &unpackSize
);
148 PRF(printf("\npackSize = %7d unpackSize = %7d ", packSize
, unpackSize
));
154 useCopyBlock
= (packSize
+ 2 >= unpackSize
|| packSize
> (1 << 16));
157 if (res
!= SZ_ERROR_OUTPUT_EOF
)
166 PRF(printf("################# COPY "));
168 while (unpackSize
> 0)
170 UInt32 u
= (unpackSize
< LZMA2_COPY_CHUNK_SIZE
) ? unpackSize
: LZMA2_COPY_CHUNK_SIZE
;
171 if (packSizeLimit
- destPos
< u
+ 3)
172 return SZ_ERROR_OUTPUT_EOF
;
173 outBuf
[destPos
++] = (Byte
)(p
->srcPos
== 0 ? LZMA2_CONTROL_COPY_RESET_DIC
: LZMA2_CONTROL_COPY_NO_RESET
);
174 outBuf
[destPos
++] = (Byte
)((u
- 1) >> 8);
175 outBuf
[destPos
++] = (Byte
)(u
- 1);
176 memcpy(outBuf
+ destPos
, LzmaEnc_GetCurBuf(p
->enc
) - unpackSize
, u
);
183 *packSizeRes
+= destPos
;
184 if (ISeqOutStream_Write(outStream
, outBuf
, destPos
) != destPos
)
185 return SZ_ERROR_WRITE
;
189 *packSizeRes
= destPos
;
190 /* needInitState = True; */
193 LzmaEnc_RestoreState(p
->enc
);
199 UInt32 u
= unpackSize
- 1;
200 UInt32 pm
= (UInt32
)(packSize
- 1);
201 unsigned mode
= (p
->srcPos
== 0) ? 3 : (p
->needInitState
? (p
->needInitProp
? 2 : 1) : 0);
205 outBuf
[destPos
++] = (Byte
)(LZMA2_CONTROL_LZMA
| (mode
<< 5) | ((u
>> 16) & 0x1F));
206 outBuf
[destPos
++] = (Byte
)(u
>> 8);
207 outBuf
[destPos
++] = (Byte
)u
;
208 outBuf
[destPos
++] = (Byte
)(pm
>> 8);
209 outBuf
[destPos
++] = (Byte
)pm
;
212 outBuf
[destPos
++] = p
->propsByte
;
214 p
->needInitProp
= False
;
215 p
->needInitState
= False
;
217 p
->srcPos
+= unpackSize
;
220 if (ISeqOutStream_Write(outStream
, outBuf
, destPos
) != destPos
)
221 return SZ_ERROR_WRITE
;
223 *packSizeRes
= destPos
;
229 /* ---------- Lzma2 Props ---------- */
231 void Lzma2EncProps_Init(CLzma2EncProps
*p
)
233 LzmaEncProps_Init(&p
->lzmaProps
);
234 p
->blockSize
= LZMA2_ENC_PROPS__BLOCK_SIZE__AUTO
;
235 p
->numBlockThreads_Reduced
= -1;
236 p
->numBlockThreads_Max
= -1;
237 p
->numTotalThreads
= -1;
240 void Lzma2EncProps_Normalize(CLzma2EncProps
*p
)
243 int t1
, t1n
, t2
, t2r
, t3
;
245 CLzmaEncProps lzmaProps
= p
->lzmaProps
;
246 LzmaEncProps_Normalize(&lzmaProps
);
247 t1n
= lzmaProps
.numThreads
;
250 t1
= p
->lzmaProps
.numThreads
;
251 t2
= p
->numBlockThreads_Max
;
252 t3
= p
->numTotalThreads
;
254 if (t2
> MTCODER__THREADS_MAX
)
255 t2
= MTCODER__THREADS_MAX
;
271 if (t2
> MTCODER__THREADS_MAX
)
272 t2
= MTCODER__THREADS_MAX
;
283 p
->lzmaProps
.numThreads
= t1
;
287 fileSize
= p
->lzmaProps
.reduceSize
;
289 if ( p
->blockSize
!= LZMA2_ENC_PROPS__BLOCK_SIZE__SOLID
290 && p
->blockSize
!= LZMA2_ENC_PROPS__BLOCK_SIZE__AUTO
291 && (p
->blockSize
< fileSize
|| fileSize
== (UInt64
)(Int64
)-1))
292 p
->lzmaProps
.reduceSize
= p
->blockSize
;
294 LzmaEncProps_Normalize(&p
->lzmaProps
);
296 p
->lzmaProps
.reduceSize
= fileSize
;
298 t1
= p
->lzmaProps
.numThreads
;
300 if (p
->blockSize
== LZMA2_ENC_PROPS__BLOCK_SIZE__SOLID
)
305 else if (p
->blockSize
== LZMA2_ENC_PROPS__BLOCK_SIZE__AUTO
&& t2
<= 1)
307 /* if there is no block multi-threading, we use SOLID block */
308 p
->blockSize
= LZMA2_ENC_PROPS__BLOCK_SIZE__SOLID
;
312 if (p
->blockSize
== LZMA2_ENC_PROPS__BLOCK_SIZE__AUTO
)
314 const UInt32 kMinSize
= (UInt32
)1 << 20;
315 const UInt32 kMaxSize
= (UInt32
)1 << 28;
316 const UInt32 dictSize
= p
->lzmaProps
.dictSize
;
317 UInt64 blockSize
= (UInt64
)dictSize
<< 2;
318 if (blockSize
< kMinSize
) blockSize
= kMinSize
;
319 if (blockSize
> kMaxSize
) blockSize
= kMaxSize
;
320 if (blockSize
< dictSize
) blockSize
= dictSize
;
321 blockSize
+= (kMinSize
- 1);
322 blockSize
&= ~(UInt64
)(kMinSize
- 1);
323 p
->blockSize
= blockSize
;
326 if (t2
> 1 && fileSize
!= (UInt64
)(Int64
)-1)
328 UInt64 numBlocks
= fileSize
/ p
->blockSize
;
329 if (numBlocks
* p
->blockSize
!= fileSize
)
331 if (numBlocks
< (unsigned)t2
)
333 t2r
= (unsigned)numBlocks
;
341 p
->numBlockThreads_Max
= t2
;
342 p
->numBlockThreads_Reduced
= t2r
;
343 p
->numTotalThreads
= t3
;
347 static SRes
Progress(ICompressProgress
*p
, UInt64 inSize
, UInt64 outSize
)
349 return (p
&& ICompressProgress_Progress(p
, inSize
, outSize
) != SZ_OK
) ? SZ_ERROR_PROGRESS
: SZ_OK
;
353 /* ---------- Lzma2 ---------- */
358 CLzma2EncProps props
;
359 UInt64 expectedDataSize
;
364 ISzAllocPtr allocBig
;
366 CLzma2EncInt coders
[MTCODER__THREADS_MAX
];
370 ISeqOutStream
*outStream
;
372 size_t outBuf_Rem
; /* remainder in outBuf */
374 size_t outBufSize
; /* size of allocated outBufs[i] */
375 size_t outBufsDataSizes
[MTCODER__BLOCKS_MAX
];
376 BoolInt mtCoder_WasConstructed
;
378 Byte
*outBufs
[MTCODER__BLOCKS_MAX
];
386 CLzma2EncHandle
Lzma2Enc_Create(ISzAllocPtr alloc
, ISzAllocPtr allocBig
)
388 CLzma2Enc
*p
= (CLzma2Enc
*)ISzAlloc_Alloc(alloc
, sizeof(CLzma2Enc
));
391 Lzma2EncProps_Init(&p
->props
);
392 Lzma2EncProps_Normalize(&p
->props
);
393 p
->expectedDataSize
= (UInt64
)(Int64
)-1;
394 p
->tempBufLzma
= NULL
;
396 p
->allocBig
= allocBig
;
399 for (i
= 0; i
< MTCODER__THREADS_MAX
; i
++)
400 p
->coders
[i
].enc
= NULL
;
404 p
->mtCoder_WasConstructed
= False
;
407 for (i
= 0; i
< MTCODER__BLOCKS_MAX
; i
++)
408 p
->outBufs
[i
] = NULL
;
419 static void Lzma2Enc_FreeOutBufs(CLzma2Enc
*p
)
422 for (i
= 0; i
< MTCODER__BLOCKS_MAX
; i
++)
425 ISzAlloc_Free(p
->alloc
, p
->outBufs
[i
]);
426 p
->outBufs
[i
] = NULL
;
434 void Lzma2Enc_Destroy(CLzma2EncHandle pp
)
436 CLzma2Enc
*p
= (CLzma2Enc
*)pp
;
438 for (i
= 0; i
< MTCODER__THREADS_MAX
; i
++)
440 CLzma2EncInt
*t
= &p
->coders
[i
];
443 LzmaEnc_Destroy(t
->enc
, p
->alloc
, p
->allocBig
);
450 if (p
->mtCoder_WasConstructed
)
452 MtCoder_Destruct(&p
->mtCoder
);
453 p
->mtCoder_WasConstructed
= False
;
455 Lzma2Enc_FreeOutBufs(p
);
458 ISzAlloc_Free(p
->alloc
, p
->tempBufLzma
);
459 p
->tempBufLzma
= NULL
;
461 ISzAlloc_Free(p
->alloc
, pp
);
465 SRes
Lzma2Enc_SetProps(CLzma2EncHandle pp
, const CLzma2EncProps
*props
)
467 CLzma2Enc
*p
= (CLzma2Enc
*)pp
;
468 CLzmaEncProps lzmaProps
= props
->lzmaProps
;
469 LzmaEncProps_Normalize(&lzmaProps
);
470 if (lzmaProps
.lc
+ lzmaProps
.lp
> LZMA2_LCLP_MAX
)
471 return SZ_ERROR_PARAM
;
473 Lzma2EncProps_Normalize(&p
->props
);
478 void Lzma2Enc_SetDataSize(CLzmaEncHandle pp
, UInt64 expectedDataSiize
)
480 CLzma2Enc
*p
= (CLzma2Enc
*)pp
;
481 p
->expectedDataSize
= expectedDataSiize
;
485 Byte
Lzma2Enc_WriteProperties(CLzma2EncHandle pp
)
487 CLzma2Enc
*p
= (CLzma2Enc
*)pp
;
489 UInt32 dicSize
= LzmaEncProps_GetDictSize(&p
->props
.lzmaProps
);
490 for (i
= 0; i
< 40; i
++)
491 if (dicSize
<= LZMA2_DIC_SIZE_FROM_PROP(i
))
497 static SRes
Lzma2Enc_EncodeMt1(
500 ISeqOutStream
*outStream
,
501 Byte
*outBuf
, size_t *outBufSize
,
502 ISeqInStream
*inStream
,
503 const Byte
*inData
, size_t inDataSize
,
505 ICompressProgress
*progress
)
507 UInt64 unpackTotal
= 0;
508 UInt64 packTotal
= 0;
510 CLimitedSeqInStream limitedInStream
;
514 outLim
= *outBufSize
;
520 p
->propsAreSet
= False
;
521 p
->enc
= LzmaEnc_Create(me
->alloc
);
526 limitedInStream
.realStream
= inStream
;
529 limitedInStream
.vt
.Read
= LimitedSeqInStream_Read
;
534 // outStream version works only in one thread. So we use CLzma2Enc::tempBufLzma
535 if (!me
->tempBufLzma
)
537 me
->tempBufLzma
= (Byte
*)ISzAlloc_Alloc(me
->alloc
, LZMA2_CHUNK_SIZE_COMPRESSED_MAX
);
538 if (!me
->tempBufLzma
)
543 RINOK(Lzma2EncInt_InitStream(p
, &me
->props
));
548 size_t inSizeCur
= 0;
550 Lzma2EncInt_InitBlock(p
);
552 LimitedSeqInStream_Init(&limitedInStream
);
553 limitedInStream
.limit
= me
->props
.blockSize
;
557 UInt64 expected
= (UInt64
)(Int64
)-1;
558 // inStream version works only in one thread. So we use CLzma2Enc::expectedDataSize
559 if (me
->expectedDataSize
!= (UInt64
)(Int64
)-1
560 && me
->expectedDataSize
>= unpackTotal
)
561 expected
= me
->expectedDataSize
- unpackTotal
;
562 if (me
->props
.blockSize
!= LZMA2_ENC_PROPS__BLOCK_SIZE__SOLID
563 && expected
> me
->props
.blockSize
)
564 expected
= (size_t)me
->props
.blockSize
;
566 LzmaEnc_SetDataSize(p
->enc
, expected
);
568 RINOK(LzmaEnc_PrepareForLzma2(p
->enc
,
570 LZMA2_KEEP_WINDOW_SIZE
,
576 inSizeCur
= inDataSize
- (size_t)unpackTotal
;
577 if (me
->props
.blockSize
!= LZMA2_ENC_PROPS__BLOCK_SIZE__SOLID
578 && inSizeCur
> me
->props
.blockSize
)
579 inSizeCur
= (size_t)me
->props
.blockSize
;
581 // LzmaEnc_SetDataSize(p->enc, inSizeCur);
583 RINOK(LzmaEnc_MemPrepare(p
->enc
,
584 inData
+ (size_t)unpackTotal
, inSizeCur
,
585 LZMA2_KEEP_WINDOW_SIZE
,
592 size_t packSize
= LZMA2_CHUNK_SIZE_COMPRESSED_MAX
;
594 packSize
= outLim
- (size_t)packTotal
;
596 res
= Lzma2EncInt_EncodeSubblock(p
,
597 outBuf
? outBuf
+ (size_t)packTotal
: me
->tempBufLzma
, &packSize
,
598 outBuf
? NULL
: outStream
);
603 packTotal
+= packSize
;
605 *outBufSize
= (size_t)packTotal
;
607 res
= Progress(progress
, unpackTotal
+ p
->srcPos
, packTotal
);
612 if (LzmaEnc_GetNumAvailableBytes(p->enc) == 0)
620 LzmaEnc_Finish(p
->enc
);
622 unpackTotal
+= p
->srcPos
;
626 if (p
->srcPos
!= (inStream
? limitedInStream
.processed
: inSizeCur
))
627 return SZ_ERROR_FAIL
;
629 if (inStream
? limitedInStream
.finished
: (unpackTotal
== inDataSize
))
635 size_t destPos
= *outBufSize
;
636 if (destPos
>= outLim
)
637 return SZ_ERROR_OUTPUT_EOF
;
639 *outBufSize
= destPos
+ 1;
644 if (ISeqOutStream_Write(outStream
, &b
, 1) != 1)
645 return SZ_ERROR_WRITE
;
657 static SRes
Lzma2Enc_MtCallback_Code(void *pp
, unsigned coderIndex
, unsigned outBufIndex
,
658 const Byte
*src
, size_t srcSize
, int finished
)
660 CLzma2Enc
*me
= (CLzma2Enc
*)pp
;
661 size_t destSize
= me
->outBufSize
;
663 CMtProgressThunk progressThunk
;
665 Byte
*dest
= me
->outBufs
[outBufIndex
];
667 me
->outBufsDataSizes
[outBufIndex
] = 0;
671 dest
= (Byte
*)ISzAlloc_Alloc(me
->alloc
, me
->outBufSize
);
674 me
->outBufs
[outBufIndex
] = dest
;
677 MtProgressThunk_CreateVTable(&progressThunk
);
678 progressThunk
.mtProgress
= &me
->mtCoder
.mtProgress
;
679 progressThunk
.inSize
= 0;
680 progressThunk
.outSize
= 0;
682 res
= Lzma2Enc_EncodeMt1(me
,
683 &me
->coders
[coderIndex
],
684 NULL
, dest
, &destSize
,
689 me
->outBufsDataSizes
[outBufIndex
] = destSize
;
695 static SRes
Lzma2Enc_MtCallback_Write(void *pp
, unsigned outBufIndex
)
697 CLzma2Enc
*me
= (CLzma2Enc
*)pp
;
698 size_t size
= me
->outBufsDataSizes
[outBufIndex
];
699 const Byte
*data
= me
->outBufs
[outBufIndex
];
702 return ISeqOutStream_Write(me
->outStream
, data
, size
) == size
? SZ_OK
: SZ_ERROR_WRITE
;
704 if (size
> me
->outBuf_Rem
)
705 return SZ_ERROR_OUTPUT_EOF
;
706 memcpy(me
->outBuf
, data
, size
);
707 me
->outBuf_Rem
-= size
;
716 SRes
Lzma2Enc_Encode2(CLzma2EncHandle pp
,
717 ISeqOutStream
*outStream
,
718 Byte
*outBuf
, size_t *outBufSize
,
719 ISeqInStream
*inStream
,
720 const Byte
*inData
, size_t inDataSize
,
721 ICompressProgress
*progress
)
723 CLzma2Enc
*p
= (CLzma2Enc
*)pp
;
725 if (inStream
&& inData
)
726 return SZ_ERROR_PARAM
;
728 if (outStream
&& outBuf
)
729 return SZ_ERROR_PARAM
;
733 for (i
= 0; i
< MTCODER__THREADS_MAX
; i
++)
734 p
->coders
[i
].propsAreSet
= False
;
739 if (p
->props
.numBlockThreads_Reduced
> 1)
741 IMtCoderCallback2 vt
;
743 if (!p
->mtCoder_WasConstructed
)
745 p
->mtCoder_WasConstructed
= True
;
746 MtCoder_Construct(&p
->mtCoder
);
749 vt
.Code
= Lzma2Enc_MtCallback_Code
;
750 vt
.Write
= Lzma2Enc_MtCallback_Write
;
752 p
->outStream
= outStream
;
758 p
->outBuf_Rem
= *outBufSize
;
762 p
->mtCoder
.allocBig
= p
->allocBig
;
763 p
->mtCoder
.progress
= progress
;
764 p
->mtCoder
.inStream
= inStream
;
765 p
->mtCoder
.inData
= inData
;
766 p
->mtCoder
.inDataSize
= inDataSize
;
767 p
->mtCoder
.mtCallback
= &vt
;
768 p
->mtCoder
.mtCallbackObject
= p
;
770 p
->mtCoder
.blockSize
= (size_t)p
->props
.blockSize
;
771 if (p
->mtCoder
.blockSize
!= p
->props
.blockSize
)
772 return SZ_ERROR_PARAM
; /* SZ_ERROR_MEM */
775 size_t destBlockSize
= p
->mtCoder
.blockSize
+ (p
->mtCoder
.blockSize
>> 10) + 16;
776 if (destBlockSize
< p
->mtCoder
.blockSize
)
777 return SZ_ERROR_PARAM
;
778 if (p
->outBufSize
!= destBlockSize
)
779 Lzma2Enc_FreeOutBufs(p
);
780 p
->outBufSize
= destBlockSize
;
783 p
->mtCoder
.numThreadsMax
= p
->props
.numBlockThreads_Max
;
784 p
->mtCoder
.expectedDataSize
= p
->expectedDataSize
;
787 SRes res
= MtCoder_Code(&p
->mtCoder
);
789 *outBufSize
= p
->outBuf
- outBuf
;
797 return Lzma2Enc_EncodeMt1(p
,
799 outStream
, outBuf
, outBufSize
,
800 inStream
, inData
, inDataSize
,