Home | History | Annotate | Download | only in C
      1 /* Lzma2Enc.c -- LZMA2 Encoder
      2 2012-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 
     40 typedef struct
     41 {
     42   CLzmaEncHandle enc;
     43   UInt64 srcPos;
     44   Byte props;
     45   Bool needInitState;
     46   Bool needInitProp;
     47 } CLzma2EncInt;
     48 
     49 static 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 
     62 SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp, ISeqInStream *inStream, UInt32 keepWindowSize,
     63     ISzAlloc *alloc, ISzAlloc *allocBig);
     64 SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen,
     65     UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig);
     66 SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit,
     67     Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize);
     68 const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp);
     69 void LzmaEnc_Finish(CLzmaEncHandle pp);
     70 void LzmaEnc_SaveState(CLzmaEncHandle pp);
     71 void LzmaEnc_RestoreState(CLzmaEncHandle pp);
     72 
     73 
     74 static 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 
    170 void Lzma2EncProps_Init(CLzma2EncProps *p)
    171 {
    172   LzmaEncProps_Init(&p->lzmaProps);
    173   p->numTotalThreads = -1;
    174   p->numBlockThreads = -1;
    175   p->blockSize = 0;
    176 }
    177 
    178 void 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 (t2 >= 0 && numBlocks < (UInt64) t2)
    242       {
    243         t2 = (UInt32)numBlocks;
    244         t3 = t1 * t2;
    245       }
    246     }
    247   }
    248   p->numBlockThreads = t2;
    249   p->numTotalThreads = t3;
    250 }
    251 
    252 static 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 
    259 typedef 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 
    280 static 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 
    320 typedef struct
    321 {
    322   IMtCoderCallback funcTable;
    323   CLzma2Enc *lzma2Enc;
    324 } CMtCallbackImp;
    325 
    326 static 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 
    383 CLzma2EncHandle 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 
    405 void 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 
    427 SRes 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 
    439 Byte 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 
    450 SRes 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 }
    494