Imported Upstream version 9.20
[platform/upstream/7zip.git] / CPP / 7zip / Compress / BZip2Encoder.cpp
1 // BZip2Encoder.cpp\r
2 \r
3 #include "StdAfx.h"\r
4 \r
5 #include "../../../C/Alloc.h"\r
6 #include "../../../C/BwtSort.h"\r
7 #include "../../../C/HuffEnc.h"\r
8 \r
9 #include "BZip2Crc.h"\r
10 #include "BZip2Encoder.h"\r
11 #include "Mtf8.h"\r
12 \r
13 namespace NCompress {\r
14 namespace NBZip2 {\r
15 \r
16 const int kMaxHuffmanLenForEncoding = 16; // it must be < kMaxHuffmanLen = 20\r
17 \r
18 static const UInt32 kBufferSize = (1 << 17);\r
19 static const int kNumHuffPasses = 4;\r
20 \r
21 bool CThreadInfo::Alloc()\r
22 {\r
23   if (m_BlockSorterIndex == 0)\r
24   {\r
25     m_BlockSorterIndex = (UInt32 *)::BigAlloc(BLOCK_SORT_BUF_SIZE(kBlockSizeMax) * sizeof(UInt32));\r
26     if (m_BlockSorterIndex == 0)\r
27       return false;\r
28   }\r
29 \r
30   if (m_Block == 0)\r
31   {\r
32     m_Block = (Byte *)::MidAlloc(kBlockSizeMax * 5 + kBlockSizeMax / 10 + (20 << 10));\r
33     if (m_Block == 0)\r
34       return false;\r
35     m_MtfArray = m_Block + kBlockSizeMax;\r
36     m_TempArray = m_MtfArray + kBlockSizeMax * 2 + 2;\r
37   }\r
38   return true;\r
39 }\r
40 \r
41 void CThreadInfo::Free()\r
42 {\r
43   ::BigFree(m_BlockSorterIndex);\r
44   m_BlockSorterIndex = 0;\r
45   ::MidFree(m_Block);\r
46   m_Block = 0;\r
47 }\r
48 \r
49 #ifndef _7ZIP_ST\r
50 \r
51 static THREAD_FUNC_DECL MFThread(void *threadCoderInfo)\r
52 {\r
53   return ((CThreadInfo *)threadCoderInfo)->ThreadFunc();\r
54 }\r
55 \r
56 #define RINOK_THREAD(x) { WRes __result_ = (x); if(__result_ != 0) return __result_; }\r
57 \r
58 HRESULT CThreadInfo::Create()\r
59 {\r
60   RINOK_THREAD(StreamWasFinishedEvent.Create());\r
61   RINOK_THREAD(WaitingWasStartedEvent.Create());\r
62   RINOK_THREAD(CanWriteEvent.Create());\r
63   RINOK_THREAD(Thread.Create(MFThread, this));\r
64   return S_OK;\r
65 }\r
66 \r
67 void CThreadInfo::FinishStream(bool needLeave)\r
68 {\r
69   Encoder->StreamWasFinished = true;\r
70   StreamWasFinishedEvent.Set();\r
71   if (needLeave)\r
72     Encoder->CS.Leave();\r
73   Encoder->CanStartWaitingEvent.Lock();\r
74   WaitingWasStartedEvent.Set();\r
75 }\r
76 \r
77 DWORD CThreadInfo::ThreadFunc()\r
78 {\r
79   for (;;)\r
80   {\r
81     Encoder->CanProcessEvent.Lock();\r
82     Encoder->CS.Enter();\r
83     if (Encoder->CloseThreads)\r
84     {\r
85       Encoder->CS.Leave();\r
86       return 0;\r
87     }\r
88     if (Encoder->StreamWasFinished)\r
89     {\r
90       FinishStream(true);\r
91       continue;\r
92     }\r
93     HRESULT res = S_OK;\r
94     bool needLeave = true;\r
95     try\r
96     {\r
97       UInt32 blockSize = Encoder->ReadRleBlock(m_Block);\r
98       m_PackSize = Encoder->m_InStream.GetProcessedSize();\r
99       m_BlockIndex = Encoder->NextBlockIndex;\r
100       if (++Encoder->NextBlockIndex == Encoder->NumThreads)\r
101         Encoder->NextBlockIndex = 0;\r
102       if (blockSize == 0)\r
103       {\r
104         FinishStream(true);\r
105         continue;\r
106       }\r
107       Encoder->CS.Leave();\r
108       needLeave = false;\r
109       res = EncodeBlock3(blockSize);\r
110     }\r
111     catch(const CInBufferException &e)  { res = e.ErrorCode; }\r
112     catch(const COutBufferException &e) { res = e.ErrorCode; }\r
113     catch(...) { res = E_FAIL; }\r
114     if (res != S_OK)\r
115     {\r
116       Encoder->Result = res;\r
117       FinishStream(needLeave);\r
118       continue;\r
119     }\r
120   }\r
121 }\r
122 \r
123 #endif\r
124 \r
125 CEncoder::CEncoder():\r
126   NumPasses(1),\r
127   m_OptimizeNumTables(false),\r
128   m_BlockSizeMult(kBlockSizeMultMax)\r
129 {\r
130   #ifndef _7ZIP_ST\r
131   ThreadsInfo = 0;\r
132   m_NumThreadsPrev = 0;\r
133   NumThreads = 1;\r
134   #endif\r
135 }\r
136 \r
137 #ifndef _7ZIP_ST\r
138 CEncoder::~CEncoder()\r
139 {\r
140   Free();\r
141 }\r
142 \r
143 HRESULT CEncoder::Create()\r
144 {\r
145   RINOK_THREAD(CanProcessEvent.CreateIfNotCreated());\r
146   RINOK_THREAD(CanStartWaitingEvent.CreateIfNotCreated());\r
147   if (ThreadsInfo != 0 && m_NumThreadsPrev == NumThreads)\r
148     return S_OK;\r
149   try\r
150   {\r
151     Free();\r
152     MtMode = (NumThreads > 1);\r
153     m_NumThreadsPrev = NumThreads;\r
154     ThreadsInfo = new CThreadInfo[NumThreads];\r
155     if (ThreadsInfo == 0)\r
156       return E_OUTOFMEMORY;\r
157   }\r
158   catch(...) { return E_OUTOFMEMORY; }\r
159   for (UInt32 t = 0; t < NumThreads; t++)\r
160   {\r
161     CThreadInfo &ti = ThreadsInfo[t];\r
162     ti.Encoder = this;\r
163     if (MtMode)\r
164     {\r
165       HRESULT res = ti.Create();\r
166       if (res != S_OK)\r
167       {\r
168         NumThreads = t;\r
169         Free();\r
170         return res;\r
171       }\r
172     }\r
173   }\r
174   return S_OK;\r
175 }\r
176 \r
177 void CEncoder::Free()\r
178 {\r
179   if (!ThreadsInfo)\r
180     return;\r
181   CloseThreads = true;\r
182   CanProcessEvent.Set();\r
183   for (UInt32 t = 0; t < NumThreads; t++)\r
184   {\r
185     CThreadInfo &ti = ThreadsInfo[t];\r
186     if (MtMode)\r
187       ti.Thread.Wait();\r
188     ti.Free();\r
189   }\r
190   delete []ThreadsInfo;\r
191   ThreadsInfo = 0;\r
192 }\r
193 #endif\r
194 \r
195 UInt32 CEncoder::ReadRleBlock(Byte *buffer)\r
196 {\r
197   UInt32 i = 0;\r
198   Byte prevByte;\r
199   if (m_InStream.ReadByte(prevByte))\r
200   {\r
201     UInt32 blockSize = m_BlockSizeMult * kBlockSizeStep - 1;\r
202     int numReps = 1;\r
203     buffer[i++] = prevByte;\r
204     while (i < blockSize) // "- 1" to support RLE\r
205     {\r
206       Byte b;\r
207       if (!m_InStream.ReadByte(b))\r
208         break;\r
209       if (b != prevByte)\r
210       {\r
211         if (numReps >= kRleModeRepSize)\r
212           buffer[i++] = (Byte)(numReps - kRleModeRepSize);\r
213         buffer[i++] = b;\r
214         numReps = 1;\r
215         prevByte = b;\r
216         continue;\r
217       }\r
218       numReps++;\r
219       if (numReps <= kRleModeRepSize)\r
220         buffer[i++] = b;\r
221       else if (numReps == kRleModeRepSize + 255)\r
222       {\r
223         buffer[i++] = (Byte)(numReps - kRleModeRepSize);\r
224         numReps = 0;\r
225       }\r
226     }\r
227     // it's to support original BZip2 decoder\r
228     if (numReps >= kRleModeRepSize)\r
229       buffer[i++] = (Byte)(numReps - kRleModeRepSize);\r
230   }\r
231   return i;\r
232 }\r
233 \r
234 void CThreadInfo::WriteBits2(UInt32 value, UInt32 numBits)\r
235   { m_OutStreamCurrent->WriteBits(value, numBits); }\r
236 void CThreadInfo::WriteByte2(Byte b) { WriteBits2(b , 8); }\r
237 void CThreadInfo::WriteBit2(bool v) { WriteBits2((v ? 1 : 0), 1); }\r
238 void CThreadInfo::WriteCrc2(UInt32 v)\r
239 {\r
240   for (int i = 0; i < 4; i++)\r
241     WriteByte2(((Byte)(v >> (24 - i * 8))));\r
242 }\r
243 \r
244 void CEncoder::WriteBits(UInt32 value, UInt32 numBits)\r
245   { m_OutStream.WriteBits(value, numBits); }\r
246 void CEncoder::WriteByte(Byte b) { WriteBits(b , 8); }\r
247 void CEncoder::WriteBit(bool v) { WriteBits((v ? 1 : 0), 1); }\r
248 void CEncoder::WriteCrc(UInt32 v)\r
249 {\r
250   for (int i = 0; i < 4; i++)\r
251     WriteByte(((Byte)(v >> (24 - i * 8))));\r
252 }\r
253 \r
254 \r
255 // blockSize > 0\r
256 void CThreadInfo::EncodeBlock(const Byte *block, UInt32 blockSize)\r
257 {\r
258   WriteBit2(false); // Randomised = false\r
259   \r
260   {\r
261     UInt32 origPtr = BlockSort(m_BlockSorterIndex, block, blockSize);\r
262     // if (m_BlockSorterIndex[origPtr] != 0) throw 1;\r
263     m_BlockSorterIndex[origPtr] = blockSize;\r
264     WriteBits2(origPtr, kNumOrigBits);\r
265   }\r
266 \r
267   CMtf8Encoder mtf;\r
268   int numInUse = 0;\r
269   {\r
270     bool inUse[256];\r
271     bool inUse16[16];\r
272     UInt32 i;\r
273     for (i = 0; i < 256; i++)\r
274       inUse[i] = false;\r
275     for (i = 0; i < 16; i++)\r
276       inUse16[i] = false;\r
277     for (i = 0; i < blockSize; i++)\r
278       inUse[block[i]] = true;\r
279     for (i = 0; i < 256; i++)\r
280       if (inUse[i])\r
281       {\r
282         inUse16[i >> 4] = true;\r
283         mtf.Buf[numInUse++] = (Byte)i;\r
284       }\r
285     for (i = 0; i < 16; i++)\r
286       WriteBit2(inUse16[i]);\r
287     for (i = 0; i < 256; i++)\r
288       if (inUse16[i >> 4])\r
289         WriteBit2(inUse[i]);\r
290   }\r
291   int alphaSize = numInUse + 2;\r
292 \r
293   Byte *mtfs = m_MtfArray;\r
294   UInt32 mtfArraySize = 0;\r
295   UInt32 symbolCounts[kMaxAlphaSize];\r
296   {\r
297     for (int i = 0; i < kMaxAlphaSize; i++)\r
298       symbolCounts[i] = 0;\r
299   }\r
300 \r
301   {\r
302     UInt32 rleSize = 0;\r
303     UInt32 i = 0;\r
304     const UInt32 *bsIndex = m_BlockSorterIndex;\r
305     block--;\r
306     do\r
307     {\r
308       int pos = mtf.FindAndMove(block[bsIndex[i]]);\r
309       if (pos == 0)\r
310         rleSize++;\r
311       else\r
312       {\r
313         while (rleSize != 0)\r
314         {\r
315           rleSize--;\r
316           mtfs[mtfArraySize++] = (Byte)(rleSize & 1);\r
317           symbolCounts[rleSize & 1]++;\r
318           rleSize >>= 1;\r
319         }\r
320         if (pos >= 0xFE)\r
321         {\r
322           mtfs[mtfArraySize++] = 0xFF;\r
323           mtfs[mtfArraySize++] = (Byte)(pos - 0xFE);\r
324         }\r
325         else\r
326           mtfs[mtfArraySize++] = (Byte)(pos + 1);\r
327         symbolCounts[pos + 1]++;\r
328       }\r
329     }\r
330     while (++i < blockSize);\r
331 \r
332     while (rleSize != 0)\r
333     {\r
334       rleSize--;\r
335       mtfs[mtfArraySize++] = (Byte)(rleSize & 1);\r
336       symbolCounts[rleSize & 1]++;\r
337       rleSize >>= 1;\r
338     }\r
339 \r
340     if (alphaSize < 256)\r
341       mtfs[mtfArraySize++] = (Byte)(alphaSize - 1);\r
342     else\r
343     {\r
344       mtfs[mtfArraySize++] = 0xFF;\r
345       mtfs[mtfArraySize++] = (Byte)(alphaSize - 256);\r
346     }\r
347     symbolCounts[alphaSize - 1]++;\r
348   }\r
349 \r
350   UInt32 numSymbols = 0;\r
351   {\r
352     for (int i = 0; i < kMaxAlphaSize; i++)\r
353       numSymbols += symbolCounts[i];\r
354   }\r
355 \r
356   int bestNumTables = kNumTablesMin;\r
357   UInt32 bestPrice = 0xFFFFFFFF;\r
358   UInt32 startPos = m_OutStreamCurrent->GetPos();\r
359   Byte startCurByte = m_OutStreamCurrent->GetCurByte();\r
360   for (int nt = kNumTablesMin; nt <= kNumTablesMax + 1; nt++)\r
361   {\r
362     int numTables;\r
363 \r
364     if(m_OptimizeNumTables)\r
365     {\r
366       m_OutStreamCurrent->SetPos(startPos);\r
367       m_OutStreamCurrent->SetCurState((startPos & 7), startCurByte);\r
368       if (nt <= kNumTablesMax)\r
369         numTables = nt;\r
370       else\r
371         numTables = bestNumTables;\r
372     }\r
373     else\r
374     {\r
375       if (numSymbols < 200)  numTables = 2;\r
376       else if (numSymbols < 600) numTables = 3;\r
377       else if (numSymbols < 1200) numTables = 4;\r
378       else if (numSymbols < 2400) numTables = 5;\r
379       else numTables = 6;\r
380     }\r
381 \r
382     WriteBits2(numTables, kNumTablesBits);\r
383     \r
384     UInt32 numSelectors = (numSymbols + kGroupSize - 1) / kGroupSize;\r
385     WriteBits2(numSelectors, kNumSelectorsBits);\r
386     \r
387     {\r
388       UInt32 remFreq = numSymbols;\r
389       int gs = 0;\r
390       int t = numTables;\r
391       do\r
392       {\r
393         UInt32 tFreq = remFreq / t;\r
394         int ge = gs;\r
395         UInt32 aFreq = 0;\r
396         while (aFreq < tFreq) //  && ge < alphaSize)\r
397           aFreq += symbolCounts[ge++];\r
398         \r
399         if (ge - 1 > gs && t != numTables && t != 1 && (((numTables - t) & 1) == 1))\r
400           aFreq -= symbolCounts[--ge];\r
401         \r
402         Byte *lens = Lens[t - 1];\r
403         int i = 0;\r
404         do\r
405           lens[i] = (i >= gs && i < ge) ? 0 : 1;\r
406         while (++i < alphaSize);\r
407         gs = ge;\r
408         remFreq -= aFreq;\r
409       }\r
410       while(--t != 0);\r
411     }\r
412     \r
413     \r
414     for (int pass = 0; pass < kNumHuffPasses; pass++)\r
415     {\r
416       {\r
417         int t = 0;\r
418         do\r
419           memset(Freqs[t], 0, sizeof(Freqs[t]));\r
420         while(++t < numTables);\r
421       }\r
422       \r
423       {\r
424         UInt32 mtfPos = 0;\r
425         UInt32 g = 0;\r
426         do\r
427         {\r
428           UInt32 symbols[kGroupSize];\r
429           int i = 0;\r
430           do\r
431           {\r
432             UInt32 symbol = mtfs[mtfPos++];\r
433             if (symbol >= 0xFF)\r
434               symbol += mtfs[mtfPos++];\r
435             symbols[i] = symbol;\r
436           }\r
437           while (++i < kGroupSize && mtfPos < mtfArraySize);\r
438           \r
439           UInt32 bestPrice = 0xFFFFFFFF;\r
440           int t = 0;\r
441           do\r
442           {\r
443             const Byte *lens = Lens[t];\r
444             UInt32 price = 0;\r
445             int j = 0;\r
446             do\r
447               price += lens[symbols[j]];\r
448             while (++j < i);\r
449             if (price < bestPrice)\r
450             {\r
451               m_Selectors[g] = (Byte)t;\r
452               bestPrice = price;\r
453             }\r
454           }\r
455           while(++t < numTables);\r
456           UInt32 *freqs = Freqs[m_Selectors[g++]];\r
457           int j = 0;\r
458           do\r
459             freqs[symbols[j]]++;\r
460           while (++j < i);\r
461         }\r
462         while (mtfPos < mtfArraySize);\r
463       }\r
464       \r
465       int t = 0;\r
466       do\r
467       {\r
468         UInt32 *freqs = Freqs[t];\r
469         int i = 0;\r
470         do\r
471           if (freqs[i] == 0)\r
472             freqs[i] = 1;\r
473         while(++i < alphaSize);\r
474         Huffman_Generate(freqs, Codes[t], Lens[t], kMaxAlphaSize, kMaxHuffmanLenForEncoding);\r
475       }\r
476       while(++t < numTables);\r
477     }\r
478     \r
479     {\r
480       Byte mtfSel[kNumTablesMax];\r
481       {\r
482         int t = 0;\r
483         do\r
484           mtfSel[t] = (Byte)t;\r
485         while(++t < numTables);\r
486       }\r
487       \r
488       UInt32 i = 0;\r
489       do\r
490       {\r
491         Byte sel = m_Selectors[i];\r
492         int pos;\r
493         for (pos = 0; mtfSel[pos] != sel; pos++)\r
494           WriteBit2(true);\r
495         WriteBit2(false);\r
496         for (; pos > 0; pos--)\r
497           mtfSel[pos] = mtfSel[pos - 1];\r
498         mtfSel[0] = sel;\r
499       }\r
500       while(++i < numSelectors);\r
501     }\r
502     \r
503     {\r
504       int t = 0;\r
505       do\r
506       {\r
507         const Byte *lens = Lens[t];\r
508         UInt32 len = lens[0];\r
509         WriteBits2(len, kNumLevelsBits);\r
510         int i = 0;\r
511         do\r
512         {\r
513           UInt32 level = lens[i];\r
514           while (len != level)\r
515           {\r
516             WriteBit2(true);\r
517             if (len < level)\r
518             {\r
519               WriteBit2(false);\r
520               len++;\r
521             }\r
522             else\r
523             {\r
524               WriteBit2(true);\r
525               len--;\r
526             }\r
527           }\r
528           WriteBit2(false);\r
529         }\r
530         while (++i < alphaSize);\r
531       }\r
532       while(++t < numTables);\r
533     }\r
534     \r
535     {\r
536       UInt32 groupSize = 0;\r
537       UInt32 groupIndex = 0;\r
538       const Byte *lens = 0;\r
539       const UInt32 *codes = 0;\r
540       UInt32 mtfPos = 0;\r
541       do\r
542       {\r
543         UInt32 symbol = mtfs[mtfPos++];\r
544         if (symbol >= 0xFF)\r
545           symbol += mtfs[mtfPos++];\r
546         if (groupSize == 0)\r
547         {\r
548           groupSize = kGroupSize;\r
549           int t = m_Selectors[groupIndex++];\r
550           lens = Lens[t];\r
551           codes = Codes[t];\r
552         }\r
553         groupSize--;\r
554         m_OutStreamCurrent->WriteBits(codes[symbol], lens[symbol]);\r
555       }\r
556       while (mtfPos < mtfArraySize);\r
557     }\r
558 \r
559     if (!m_OptimizeNumTables)\r
560       break;\r
561     UInt32 price = m_OutStreamCurrent->GetPos() - startPos;\r
562     if (price <= bestPrice)\r
563     {\r
564       if (nt == kNumTablesMax)\r
565         break;\r
566       bestPrice = price;\r
567       bestNumTables = nt;\r
568     }\r
569   }\r
570 }\r
571 \r
572 // blockSize > 0\r
573 UInt32 CThreadInfo::EncodeBlockWithHeaders(const Byte *block, UInt32 blockSize)\r
574 {\r
575   WriteByte2(kBlockSig0);\r
576   WriteByte2(kBlockSig1);\r
577   WriteByte2(kBlockSig2);\r
578   WriteByte2(kBlockSig3);\r
579   WriteByte2(kBlockSig4);\r
580   WriteByte2(kBlockSig5);\r
581 \r
582   CBZip2Crc crc;\r
583   int numReps = 0;\r
584   Byte prevByte = block[0];\r
585   UInt32 i = 0;\r
586   do\r
587   {\r
588     Byte b = block[i];\r
589     if (numReps == kRleModeRepSize)\r
590     {\r
591       for (; b > 0; b--)\r
592         crc.UpdateByte(prevByte);\r
593       numReps = 0;\r
594       continue;\r
595     }\r
596     if (prevByte == b)\r
597       numReps++;\r
598     else\r
599     {\r
600       numReps = 1;\r
601       prevByte = b;\r
602     }\r
603     crc.UpdateByte(b);\r
604   }\r
605   while (++i < blockSize);\r
606   UInt32 crcRes = crc.GetDigest();\r
607   WriteCrc2(crcRes);\r
608   EncodeBlock(block, blockSize);\r
609   return crcRes;\r
610 }\r
611 \r
612 void CThreadInfo::EncodeBlock2(const Byte *block, UInt32 blockSize, UInt32 numPasses)\r
613 {\r
614   UInt32 numCrcs = m_NumCrcs;\r
615   bool needCompare = false;\r
616 \r
617   UInt32 startBytePos = m_OutStreamCurrent->GetBytePos();\r
618   UInt32 startPos = m_OutStreamCurrent->GetPos();\r
619   Byte startCurByte = m_OutStreamCurrent->GetCurByte();\r
620   Byte endCurByte = 0;\r
621   UInt32 endPos = 0;\r
622   if (numPasses > 1 && blockSize >= (1 << 10))\r
623   {\r
624     UInt32 blockSize0 = blockSize / 2;\r
625     for (;(block[blockSize0] == block[blockSize0 - 1] ||\r
626           block[blockSize0 - 1] == block[blockSize0 - 2]) &&\r
627           blockSize0 < blockSize; blockSize0++);\r
628     if (blockSize0 < blockSize)\r
629     {\r
630       EncodeBlock2(block, blockSize0, numPasses - 1);\r
631       EncodeBlock2(block + blockSize0, blockSize - blockSize0, numPasses - 1);\r
632       endPos = m_OutStreamCurrent->GetPos();\r
633       endCurByte = m_OutStreamCurrent->GetCurByte();\r
634       if ((endPos & 7) > 0)\r
635         WriteBits2(0, 8 - (endPos & 7));\r
636       m_OutStreamCurrent->SetCurState((startPos & 7), startCurByte);\r
637       needCompare = true;\r
638     }\r
639   }\r
640 \r
641   UInt32 startBytePos2 = m_OutStreamCurrent->GetBytePos();\r
642   UInt32 startPos2 = m_OutStreamCurrent->GetPos();\r
643   UInt32 crcVal = EncodeBlockWithHeaders(block, blockSize);\r
644   UInt32 endPos2 = m_OutStreamCurrent->GetPos();\r
645 \r
646   if (needCompare)\r
647   {\r
648     UInt32 size2 = endPos2 - startPos2;\r
649     if (size2 < endPos - startPos)\r
650     {\r
651       UInt32 numBytes = m_OutStreamCurrent->GetBytePos() - startBytePos2;\r
652       Byte *buffer = m_OutStreamCurrent->GetStream();\r
653       for (UInt32 i = 0; i < numBytes; i++)\r
654         buffer[startBytePos + i] = buffer[startBytePos2 + i];\r
655       m_OutStreamCurrent->SetPos(startPos + endPos2 - startPos2);\r
656       m_NumCrcs = numCrcs;\r
657       m_CRCs[m_NumCrcs++] = crcVal;\r
658     }\r
659     else\r
660     {\r
661       m_OutStreamCurrent->SetPos(endPos);\r
662       m_OutStreamCurrent->SetCurState((endPos & 7), endCurByte);\r
663     }\r
664   }\r
665   else\r
666   {\r
667     m_NumCrcs = numCrcs;\r
668     m_CRCs[m_NumCrcs++] = crcVal;\r
669   }\r
670 }\r
671 \r
672 HRESULT CThreadInfo::EncodeBlock3(UInt32 blockSize)\r
673 {\r
674   CMsbfEncoderTemp outStreamTemp;\r
675   outStreamTemp.SetStream(m_TempArray);\r
676   outStreamTemp.Init();\r
677   m_OutStreamCurrent = &outStreamTemp;\r
678 \r
679   m_NumCrcs = 0;\r
680 \r
681   EncodeBlock2(m_Block, blockSize, Encoder->NumPasses);\r
682 \r
683   #ifndef _7ZIP_ST\r
684   if (Encoder->MtMode)\r
685     Encoder->ThreadsInfo[m_BlockIndex].CanWriteEvent.Lock();\r
686   #endif\r
687   for (UInt32 i = 0; i < m_NumCrcs; i++)\r
688     Encoder->CombinedCrc.Update(m_CRCs[i]);\r
689   Encoder->WriteBytes(m_TempArray, outStreamTemp.GetPos(), outStreamTemp.GetCurByte());\r
690   HRESULT res = S_OK;\r
691   #ifndef _7ZIP_ST\r
692   if (Encoder->MtMode)\r
693   {\r
694     UInt32 blockIndex = m_BlockIndex + 1;\r
695     if (blockIndex == Encoder->NumThreads)\r
696       blockIndex = 0;\r
697 \r
698     if (Encoder->Progress)\r
699     {\r
700       UInt64 unpackSize = Encoder->m_OutStream.GetProcessedSize();\r
701       res = Encoder->Progress->SetRatioInfo(&m_PackSize, &unpackSize);\r
702     }\r
703 \r
704     Encoder->ThreadsInfo[blockIndex].CanWriteEvent.Set();\r
705   }\r
706   #endif\r
707   return res;\r
708 }\r
709 \r
710 void CEncoder::WriteBytes(const Byte *data, UInt32 sizeInBits, Byte lastByte)\r
711 {\r
712   UInt32 bytesSize = (sizeInBits / 8);\r
713   for (UInt32 i = 0; i < bytesSize; i++)\r
714     m_OutStream.WriteBits(data[i], 8);\r
715   WriteBits(lastByte, (sizeInBits & 7));\r
716 }\r
717 \r
718 \r
719 HRESULT CEncoder::CodeReal(ISequentialInStream *inStream, ISequentialOutStream *outStream,\r
720     const UInt64 * /* inSize */, const UInt64 * /* outSize */, ICompressProgressInfo *progress)\r
721 {\r
722   #ifndef _7ZIP_ST\r
723   Progress = progress;\r
724   RINOK(Create());\r
725   for (UInt32 t = 0; t < NumThreads; t++)\r
726   #endif\r
727   {\r
728     #ifndef _7ZIP_ST\r
729     CThreadInfo &ti = ThreadsInfo[t];\r
730     if (MtMode)\r
731     {\r
732       RINOK(ti.StreamWasFinishedEvent.Reset());\r
733       RINOK(ti.WaitingWasStartedEvent.Reset());\r
734       RINOK(ti.CanWriteEvent.Reset());\r
735     }\r
736     #else\r
737     CThreadInfo &ti = ThreadsInfo;\r
738     ti.Encoder = this;\r
739     #endif\r
740 \r
741     ti.m_OptimizeNumTables = m_OptimizeNumTables;\r
742 \r
743     if (!ti.Alloc())\r
744       return E_OUTOFMEMORY;\r
745   }\r
746 \r
747 \r
748   if (!m_InStream.Create(kBufferSize))\r
749     return E_OUTOFMEMORY;\r
750   if (!m_OutStream.Create(kBufferSize))\r
751     return E_OUTOFMEMORY;\r
752 \r
753 \r
754   m_InStream.SetStream(inStream);\r
755   m_InStream.Init();\r
756 \r
757   m_OutStream.SetStream(outStream);\r
758   m_OutStream.Init();\r
759 \r
760   CFlusher flusher(this);\r
761 \r
762   CombinedCrc.Init();\r
763   #ifndef _7ZIP_ST\r
764   NextBlockIndex = 0;\r
765   StreamWasFinished = false;\r
766   CloseThreads = false;\r
767   CanStartWaitingEvent.Reset();\r
768   #endif\r
769 \r
770   WriteByte(kArSig0);\r
771   WriteByte(kArSig1);\r
772   WriteByte(kArSig2);\r
773   WriteByte((Byte)(kArSig3 + m_BlockSizeMult));\r
774 \r
775   #ifndef _7ZIP_ST\r
776 \r
777   if (MtMode)\r
778   {\r
779     ThreadsInfo[0].CanWriteEvent.Set();\r
780     Result = S_OK;\r
781     CanProcessEvent.Set();\r
782     UInt32 t;\r
783     for (t = 0; t < NumThreads; t++)\r
784       ThreadsInfo[t].StreamWasFinishedEvent.Lock();\r
785     CanProcessEvent.Reset();\r
786     CanStartWaitingEvent.Set();\r
787     for (t = 0; t < NumThreads; t++)\r
788       ThreadsInfo[t].WaitingWasStartedEvent.Lock();\r
789     CanStartWaitingEvent.Reset();\r
790     RINOK(Result);\r
791   }\r
792   else\r
793   #endif\r
794   {\r
795     for (;;)\r
796     {\r
797       CThreadInfo &ti =\r
798       #ifndef _7ZIP_ST\r
799       ThreadsInfo[0];\r
800       #else\r
801       ThreadsInfo;\r
802       #endif\r
803       UInt32 blockSize = ReadRleBlock(ti.m_Block);\r
804       if (blockSize == 0)\r
805         break;\r
806       RINOK(ti.EncodeBlock3(blockSize));\r
807       if (progress)\r
808       {\r
809         UInt64 packSize = m_InStream.GetProcessedSize();\r
810         UInt64 unpackSize = m_OutStream.GetProcessedSize();\r
811         RINOK(progress->SetRatioInfo(&packSize, &unpackSize));\r
812       }\r
813     }\r
814   }\r
815   WriteByte(kFinSig0);\r
816   WriteByte(kFinSig1);\r
817   WriteByte(kFinSig2);\r
818   WriteByte(kFinSig3);\r
819   WriteByte(kFinSig4);\r
820   WriteByte(kFinSig5);\r
821 \r
822   WriteCrc(CombinedCrc.GetDigest());\r
823   return Flush();\r
824 }\r
825 \r
826 STDMETHODIMP CEncoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,\r
827     const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress)\r
828 {\r
829   try { return CodeReal(inStream, outStream, inSize, outSize, progress); }\r
830   catch(const CInBufferException &e) { return e.ErrorCode; }\r
831   catch(const COutBufferException &e) { return e.ErrorCode; }\r
832   catch(...) { return S_FALSE; }\r
833 }\r
834 \r
835 HRESULT CEncoder::SetCoderProperties(const PROPID *propIDs, const PROPVARIANT *props, UInt32 numProps)\r
836 {\r
837   for(UInt32 i = 0; i < numProps; i++)\r
838   {\r
839     const PROPVARIANT &prop = props[i];\r
840     switch(propIDs[i])\r
841     {\r
842       case NCoderPropID::kNumPasses:\r
843       {\r
844         if (prop.vt != VT_UI4)\r
845           return E_INVALIDARG;\r
846         UInt32 numPasses = prop.ulVal;\r
847         if (numPasses == 0)\r
848           numPasses = 1;\r
849         if (numPasses > kNumPassesMax)\r
850           numPasses = kNumPassesMax;\r
851         NumPasses = numPasses;\r
852         m_OptimizeNumTables = (NumPasses > 1);\r
853         break;\r
854       }\r
855       case NCoderPropID::kDictionarySize:\r
856       {\r
857         if (prop.vt != VT_UI4)\r
858           return E_INVALIDARG;\r
859         UInt32 dictionary = prop.ulVal / kBlockSizeStep;\r
860         if (dictionary < kBlockSizeMultMin)\r
861           dictionary = kBlockSizeMultMin;\r
862         else if (dictionary > kBlockSizeMultMax)\r
863           dictionary = kBlockSizeMultMax;\r
864         m_BlockSizeMult = dictionary;\r
865         break;\r
866       }\r
867       case NCoderPropID::kNumThreads:\r
868       {\r
869         #ifndef _7ZIP_ST\r
870         if (prop.vt != VT_UI4)\r
871           return E_INVALIDARG;\r
872         NumThreads = prop.ulVal;\r
873         if (NumThreads < 1)\r
874           NumThreads = 1;\r
875         #endif\r
876         break;\r
877       }\r
878       default:\r
879         return E_INVALIDARG;\r
880     }\r
881   }\r
882   return S_OK;\r
883 }\r
884 \r
885 #ifndef _7ZIP_ST\r
886 STDMETHODIMP CEncoder::SetNumberOfThreads(UInt32 numThreads)\r
887 {\r
888   NumThreads = numThreads;\r
889   if (NumThreads < 1)\r
890     NumThreads = 1;\r
891   return S_OK;\r
892 }\r
893 #endif\r
894 \r
895 }}\r