/src/unrar/unpack50mt.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // 2023.09.09: 0x400000 and 2 are optimal for i9-12900K. |
2 | | // Further increasing the buffer size reduced the extraction speed. |
3 | 9.20k | #define UNP_READ_SIZE_MT 0x400000 |
4 | 46.4k | #define UNP_BLOCKS_PER_THREAD 2 |
5 | | |
6 | | |
7 | | struct UnpackThreadDataList |
8 | | { |
9 | | UnpackThreadData *D; |
10 | | uint BlockCount; |
11 | | }; |
12 | | |
13 | | |
14 | | THREAD_PROC(UnpackDecodeThread) |
15 | 5 | { |
16 | 5 | UnpackThreadDataList *DL=(UnpackThreadDataList *)Data; |
17 | 10 | for (uint I=0;I<DL->BlockCount;I++) |
18 | 5 | DL->D->UnpackPtr->UnpackDecode(DL->D[I]); |
19 | 5 | } |
20 | | |
21 | | |
22 | | void Unpack::InitMT() |
23 | 2.42k | { |
24 | 2.42k | if (ReadBufMT==NULL) |
25 | 2.24k | { |
26 | | // Even getbits32 can read up to 3 additional bytes after current |
27 | | // and our block header and table reading code can look much further. |
28 | | // Let's allocate the additional space here, so we do not need to check |
29 | | // bounds for every bit field access. |
30 | 2.24k | const size_t Overflow=1024; |
31 | | |
32 | 2.24k | ReadBufMT=new byte[UNP_READ_SIZE_MT+Overflow]; |
33 | 2.24k | memset(ReadBufMT,0,UNP_READ_SIZE_MT+Overflow); |
34 | 2.24k | } |
35 | 2.42k | if (UnpThreadData==NULL) |
36 | 2.24k | { |
37 | 2.24k | uint MaxItems=MaxUserThreads*UNP_BLOCKS_PER_THREAD; |
38 | 2.24k | UnpThreadData=new UnpackThreadData[MaxItems]; |
39 | 2.24k | memset(UnpThreadData,0,sizeof(UnpackThreadData)*MaxItems); |
40 | | |
41 | 38.0k | for (uint I=0;I<MaxItems;I++) |
42 | 35.8k | { |
43 | 35.8k | UnpackThreadData *CurData=UnpThreadData+I; |
44 | 35.8k | if (CurData->Decoded==NULL) |
45 | 35.8k | { |
46 | | // Typical number of items in RAR blocks does not exceed 0x4000. |
47 | 35.8k | CurData->DecodedAllocated=0x4100; |
48 | | // It will be freed in the object destructor, not in this file. |
49 | 35.8k | CurData->Decoded=(UnpackDecodedItem *)malloc(CurData->DecodedAllocated*sizeof(UnpackDecodedItem)); |
50 | 35.8k | if (CurData->Decoded==NULL) |
51 | 0 | ErrHandler.MemoryError(); |
52 | 35.8k | } |
53 | 35.8k | } |
54 | 2.24k | } |
55 | 2.42k | } |
56 | | |
57 | | |
58 | | void Unpack::Unpack5MT(bool Solid) |
59 | 2.42k | { |
60 | 2.42k | InitMT(); |
61 | 2.42k | UnpInitData(Solid); |
62 | | |
63 | 41.2k | for (uint I=0;I<MaxUserThreads*UNP_BLOCKS_PER_THREAD;I++) |
64 | 38.8k | { |
65 | 38.8k | UnpackThreadData *CurData=UnpThreadData+I; |
66 | 38.8k | CurData->LargeBlock=false; |
67 | 38.8k | CurData->Incomplete=false; |
68 | 38.8k | } |
69 | | |
70 | 2.42k | UnpThreadData[0].BlockHeader=BlockHeader; |
71 | 2.42k | UnpThreadData[0].BlockTables=BlockTables; |
72 | 2.42k | uint LastBlockNum=0; |
73 | | |
74 | 2.42k | int DataSize=0; |
75 | 2.42k | int BlockStart=0; |
76 | | |
77 | | |
78 | | // 'true' if we found a block too large for multithreaded extraction, |
79 | | // so we switched to single threaded mode until the end of file. |
80 | | // Large blocks could cause too high memory use in multithreaded mode. |
81 | 2.42k | bool LargeBlock=false; |
82 | | |
83 | 2.42k | bool Done=false; |
84 | 7.13k | while (!Done) |
85 | 4.72k | { |
86 | | // Data amount, which is guaranteed to fit block header and tables, |
87 | | // so we can safely read them without additional checks. |
88 | 4.72k | const int TooSmallToProcess=1024; |
89 | | |
90 | 4.72k | int ReadSize=UnpIO->UnpRead(ReadBufMT+DataSize,(UNP_READ_SIZE_MT-DataSize)&~0xf); |
91 | 4.72k | if (ReadSize<0) |
92 | 2 | break; |
93 | 4.72k | DataSize+=ReadSize; |
94 | 4.72k | if (DataSize==0) |
95 | 12 | break; |
96 | | |
97 | | // First read chunk can be small if we are near the end of volume |
98 | | // and we want it to fit block header and tables. |
99 | 4.71k | if (ReadSize>0 && DataSize<TooSmallToProcess) |
100 | 1.72k | continue; |
101 | | |
102 | 2.98k | while (BlockStart<DataSize && !Done) |
103 | 2.98k | { |
104 | 2.98k | uint BlockNumber=0,BlockNumberMT=0; |
105 | 2.99k | while (BlockNumber<MaxUserThreads*UNP_BLOCKS_PER_THREAD) |
106 | 2.99k | { |
107 | 2.99k | UnpackThreadData *CurData=UnpThreadData+BlockNumber; |
108 | 2.99k | LastBlockNum=BlockNumber; |
109 | 2.99k | CurData->UnpackPtr=this; |
110 | | |
111 | | // 'Incomplete' thread is present. This is a thread processing block |
112 | | // in the end of buffer, split between two read operations. |
113 | 2.99k | if (CurData->Incomplete) |
114 | 555 | CurData->DataSize=DataSize; |
115 | 2.43k | else |
116 | 2.43k | { |
117 | 2.43k | CurData->Inp.SetExternalBuffer(ReadBufMT+BlockStart); |
118 | 2.43k | CurData->Inp.InitBitInput(); |
119 | 2.43k | CurData->DataSize=DataSize-BlockStart; |
120 | 2.43k | if (CurData->DataSize==0) |
121 | 0 | break; |
122 | 2.43k | CurData->DamagedData=false; |
123 | 2.43k | CurData->HeaderRead=false; |
124 | 2.43k | CurData->TableRead=false; |
125 | 2.43k | } |
126 | | |
127 | | // We should not use 'last block in file' block flag here unless |
128 | | // we'll check the block size, because even if block is last in file, |
129 | | // it can exceed the current buffer and require more reading. |
130 | 2.99k | CurData->NoDataLeft=(ReadSize==0); |
131 | | |
132 | 2.99k | CurData->Incomplete=false; |
133 | 2.99k | CurData->ThreadNumber=BlockNumber; |
134 | | |
135 | 2.99k | if (!CurData->HeaderRead) |
136 | 2.43k | { |
137 | 2.43k | CurData->HeaderRead=true; |
138 | 2.43k | if (!ReadBlockHeader(CurData->Inp,CurData->BlockHeader) || |
139 | 2.43k | !CurData->BlockHeader.TablePresent && !TablesRead5) |
140 | 292 | { |
141 | 292 | Done=true; |
142 | 292 | break; |
143 | 292 | } |
144 | 2.14k | TablesRead5=true; |
145 | 2.14k | } |
146 | | |
147 | | // To prevent too high memory use we switch to single threaded mode |
148 | | // if block exceeds this size. Typically RAR blocks do not exceed |
149 | | // 64 KB, so this protection should not affect most of valid archives. |
150 | 2.70k | const int LargeBlockSize=0x20000; |
151 | 2.70k | if (LargeBlock || CurData->BlockHeader.BlockSize>LargeBlockSize) |
152 | 1.50k | LargeBlock=CurData->LargeBlock=true; |
153 | 1.19k | else |
154 | 1.19k | BlockNumberMT++; // Number of normal blocks processed in MT mode. |
155 | | |
156 | 2.70k | BlockStart+=CurData->BlockHeader.HeaderSize+CurData->BlockHeader.BlockSize; |
157 | | |
158 | 2.70k | BlockNumber++; |
159 | | |
160 | 2.70k | int DataLeft=DataSize-BlockStart; |
161 | 2.70k | if (DataLeft>=0 && CurData->BlockHeader.LastBlockInFile) |
162 | 136 | break; |
163 | | |
164 | | // For second and following threads we move smaller blocks to buffer |
165 | | // start to ensure that we have enough data to fit block header |
166 | | // and tables. |
167 | 2.56k | if (DataLeft<TooSmallToProcess) |
168 | 2.56k | break; |
169 | 2.56k | } |
170 | | |
171 | | //#undef USE_THREADS |
172 | 2.98k | UnpackThreadDataList UTDArray[MaxPoolThreads]; |
173 | 2.98k | uint UTDArrayPos=0; |
174 | | |
175 | 2.98k | uint MaxBlockPerThread=BlockNumberMT/MaxUserThreads; |
176 | 2.98k | if (BlockNumberMT%MaxUserThreads!=0) |
177 | 1.19k | MaxBlockPerThread++; |
178 | | |
179 | | // Decode all normal blocks until the first 'large' if any. |
180 | 4.18k | for (uint CurBlock=0;CurBlock<BlockNumberMT;CurBlock+=MaxBlockPerThread) |
181 | 1.19k | { |
182 | 1.19k | UnpackThreadDataList *UTD=UTDArray+UTDArrayPos++; |
183 | 1.19k | UTD->D=UnpThreadData+CurBlock; |
184 | 1.19k | UTD->BlockCount=Min(MaxBlockPerThread,BlockNumberMT-CurBlock); |
185 | | |
186 | 1.19k | #ifdef USE_THREADS |
187 | 1.19k | if (BlockNumber==1) |
188 | 1.18k | UnpackDecode(*UTD->D); |
189 | 5 | else |
190 | 5 | UnpThreadPool->AddTask(UnpackDecodeThread,(void*)UTD); |
191 | | #else |
192 | | for (uint I=0;I<UTD->BlockCount;I++) |
193 | | UnpackDecode(UTD->D[I]); |
194 | | #endif |
195 | 1.19k | } |
196 | | |
197 | 2.98k | if (BlockNumber==0) |
198 | 290 | break; |
199 | | |
200 | 2.69k | #ifdef USE_THREADS |
201 | 2.69k | UnpThreadPool->WaitDone(); |
202 | 2.69k | #endif |
203 | | |
204 | 2.69k | bool IncompleteThread=false; |
205 | | |
206 | 2.72k | for (uint Block=0;Block<BlockNumber;Block++) |
207 | 2.70k | { |
208 | 2.70k | UnpackThreadData *CurData=UnpThreadData+Block; |
209 | 2.70k | if (!CurData->LargeBlock && !ProcessDecoded(*CurData) || |
210 | 2.70k | CurData->LargeBlock && !UnpackLargeBlock(*CurData) || |
211 | 2.70k | CurData->DamagedData) |
212 | 82 | { |
213 | 82 | Done=true; |
214 | 82 | break; |
215 | 82 | } |
216 | 2.61k | if (CurData->Incomplete) |
217 | 2.46k | { |
218 | 2.46k | int BufPos=int(CurData->Inp.InBuf+CurData->Inp.InAddr-ReadBufMT); |
219 | 2.46k | if (DataSize<=BufPos) // Thread exceeded input buffer boundary. |
220 | 1.91k | { |
221 | 1.91k | Done=true; |
222 | 1.91k | break; |
223 | 1.91k | } |
224 | 555 | IncompleteThread=true; |
225 | 555 | memmove(ReadBufMT,ReadBufMT+BufPos,DataSize-BufPos); |
226 | 555 | CurData->BlockHeader.BlockSize-=CurData->Inp.InAddr-CurData->BlockHeader.BlockStart; |
227 | 555 | CurData->BlockHeader.HeaderSize=0; |
228 | 555 | CurData->BlockHeader.BlockStart=0; |
229 | 555 | CurData->Inp.InBuf=ReadBufMT; |
230 | 555 | CurData->Inp.InAddr=0; |
231 | | |
232 | 555 | if (Block!=0) |
233 | 1 | { |
234 | | // Move the incomplete thread entry to the first position, |
235 | | // so we'll start processing from it. Preserve the original |
236 | | // buffer for decoded data. |
237 | 1 | UnpackDecodedItem *Decoded=UnpThreadData[0].Decoded; |
238 | 1 | uint DecodedAllocated=UnpThreadData[0].DecodedAllocated; |
239 | 1 | UnpThreadData[0]=*CurData; |
240 | 1 | UnpThreadData[0].Decoded=Decoded; |
241 | 1 | UnpThreadData[0].DecodedAllocated=DecodedAllocated; |
242 | 1 | CurData->Incomplete=false; |
243 | 1 | } |
244 | | |
245 | 555 | BlockStart=0; |
246 | 555 | DataSize-=BufPos; |
247 | 555 | break; |
248 | 2.46k | } |
249 | 154 | else |
250 | 154 | if (CurData->BlockHeader.LastBlockInFile) |
251 | 126 | { |
252 | 126 | Done=true; |
253 | 126 | break; |
254 | 126 | } |
255 | 2.61k | } |
256 | | |
257 | 2.69k | if (IncompleteThread || Done) |
258 | 2.67k | break; // Current buffer is done, read more data or quit. |
259 | 23 | else |
260 | 23 | { |
261 | 23 | int DataLeft=DataSize-BlockStart; |
262 | 23 | if (DataLeft<TooSmallToProcess) |
263 | 23 | { |
264 | 23 | if (DataLeft<0) // Invalid data, must not happen in valid archive. |
265 | 2 | { |
266 | 2 | Done=true; |
267 | 2 | break; |
268 | 2 | } |
269 | | |
270 | | // If we do not have incomplete thread and have some data |
271 | | // in the end of buffer, too small for single thread, |
272 | | // let's move it to beginning of next buffer. |
273 | 21 | if (DataLeft>0) |
274 | 19 | memmove(ReadBufMT,ReadBufMT+BlockStart,DataLeft); |
275 | 21 | DataSize=DataLeft; |
276 | 21 | BlockStart=0; |
277 | 21 | break; // Current buffer is done, try to read more data. |
278 | 23 | } |
279 | 23 | } |
280 | 2.69k | } |
281 | 2.98k | } |
282 | 2.42k | UnpPtr=WrapUp(UnpPtr); // ProcessDecoded and maybe others can leave UnpPtr >= MaxWinSize here. |
283 | 2.42k | UnpWriteBuf(); |
284 | | |
285 | 2.42k | BlockHeader=UnpThreadData[LastBlockNum].BlockHeader; |
286 | 2.42k | BlockTables=UnpThreadData[LastBlockNum].BlockTables; |
287 | 2.42k | } |
288 | | |
289 | | |
290 | | // Decode Huffman block and save decoded data to memory. |
291 | | void Unpack::UnpackDecode(UnpackThreadData &D) |
292 | 1.19k | { |
293 | 1.19k | if (!D.TableRead) |
294 | 1.04k | { |
295 | 1.04k | D.TableRead=true; |
296 | 1.04k | if (!ReadTables(D.Inp,D.BlockHeader,D.BlockTables)) |
297 | 4 | { |
298 | 4 | D.DamagedData=true; |
299 | 4 | return; |
300 | 4 | } |
301 | 1.04k | } |
302 | | |
303 | 1.18k | if (D.Inp.InAddr>D.BlockHeader.HeaderSize+D.BlockHeader.BlockSize) |
304 | 14 | { |
305 | 14 | D.DamagedData=true; |
306 | 14 | return; |
307 | 14 | } |
308 | | |
309 | 1.17k | D.DecodedSize=0; |
310 | 1.17k | int BlockBorder=D.BlockHeader.BlockStart+D.BlockHeader.BlockSize-1; |
311 | | |
312 | | // Reserve enough space even for filter entry. |
313 | 1.17k | int DataBorder=D.DataSize-16; |
314 | 1.17k | int ReadBorder=Min(BlockBorder,DataBorder); |
315 | | |
316 | 3.44M | while (true) |
317 | 3.44M | { |
318 | 3.44M | if (D.Inp.InAddr>=ReadBorder) |
319 | 15.8k | { |
320 | 15.8k | if (D.Inp.InAddr>BlockBorder || D.Inp.InAddr==BlockBorder && |
321 | 15.7k | D.Inp.InBit>=D.BlockHeader.BlockBitSize) |
322 | 164 | break; |
323 | | |
324 | | // If we do not have any more data in file to read, we must process |
325 | | // what we have until last byte. Otherwise we can return and append |
326 | | // more data to unprocessed few bytes. |
327 | 15.6k | if ((D.Inp.InAddr>=DataBorder) && !D.NoDataLeft || D.Inp.InAddr>=D.DataSize) |
328 | 1.01k | { |
329 | 1.01k | D.Incomplete=true; |
330 | 1.01k | break; |
331 | 1.01k | } |
332 | 15.6k | } |
333 | 3.43M | if (D.DecodedSize>D.DecodedAllocated-8) // Filter can use several slots. |
334 | 18 | { |
335 | 18 | D.DecodedAllocated=D.DecodedAllocated*2; |
336 | 18 | void *Decoded=realloc(D.Decoded,D.DecodedAllocated*sizeof(UnpackDecodedItem)); |
337 | 18 | if (Decoded==NULL) |
338 | 0 | ErrHandler.MemoryError(); // D.Decoded will be freed in the destructor. |
339 | 18 | D.Decoded=(UnpackDecodedItem *)Decoded; |
340 | 18 | } |
341 | | |
342 | 3.43M | UnpackDecodedItem *CurItem=D.Decoded+D.DecodedSize++; |
343 | | |
344 | 3.43M | uint MainSlot=DecodeNumber(D.Inp,&D.BlockTables.LD); |
345 | 3.43M | if (MainSlot<256) |
346 | 3.04M | { |
347 | 3.04M | if (D.DecodedSize>1) |
348 | 3.04M | { |
349 | 3.04M | UnpackDecodedItem *PrevItem=CurItem-1; |
350 | 3.04M | if (PrevItem->Type==UNPDT_LITERAL && PrevItem->Length<ASIZE(PrevItem->Literal)-1) |
351 | 2.64M | { |
352 | 2.64M | PrevItem->Length++; |
353 | 2.64M | PrevItem->Literal[PrevItem->Length]=(byte)MainSlot; |
354 | 2.64M | D.DecodedSize--; |
355 | 2.64M | continue; |
356 | 2.64M | } |
357 | 3.04M | } |
358 | 406k | CurItem->Type=UNPDT_LITERAL; |
359 | 406k | CurItem->Literal[0]=(byte)MainSlot; |
360 | 406k | CurItem->Length=0; |
361 | 406k | continue; |
362 | 3.04M | } |
363 | 391k | if (MainSlot>=262) |
364 | 89.9k | { |
365 | 89.9k | uint Length=SlotToLength(D.Inp,MainSlot-262); |
366 | | |
367 | 89.9k | size_t Distance=1; |
368 | 89.9k | uint DBits,DistSlot=DecodeNumber(D.Inp,&D.BlockTables.DD); |
369 | 89.9k | if (DistSlot<4) |
370 | 67.3k | { |
371 | 67.3k | DBits=0; |
372 | 67.3k | Distance+=DistSlot; |
373 | 67.3k | } |
374 | 22.5k | else |
375 | 22.5k | { |
376 | 22.5k | DBits=DistSlot/2 - 1; |
377 | 22.5k | Distance+=size_t(2 | (DistSlot & 1)) << DBits; |
378 | 22.5k | } |
379 | | |
380 | 89.9k | if (DBits>0) |
381 | 22.5k | { |
382 | 22.5k | if (DBits>=4) |
383 | 11.6k | { |
384 | 11.6k | if (DBits>4) |
385 | 8.76k | { |
386 | | // It is also possible to always use getbits64() here. |
387 | 8.76k | if (DBits>36) |
388 | 326 | Distance+=( ( size_t(D.Inp.getbits64() ) >> (68-DBits) ) << 4 ); |
389 | 8.43k | else |
390 | 8.43k | Distance+=( ( size_t(D.Inp.getbits32() ) >> (36-DBits) ) << 4 ); |
391 | 8.76k | D.Inp.addbits(DBits-4); |
392 | 8.76k | } |
393 | 11.6k | uint LowDist=DecodeNumber(D.Inp,&D.BlockTables.LDD); |
394 | 11.6k | Distance+=LowDist; |
395 | | |
396 | | // Distance can be 0 for multiples of 4 GB as result of size_t |
397 | | // overflow in 32-bit build. Its lower 32-bit can also erroneously |
398 | | // fit into dictionary after truncating upper 32-bits. Replace such |
399 | | // invalid distances with -1, so CopyString sets 0 data for them. |
400 | | // DBits>=30 also as DistSlot>=62 indicate distances >=0x80000001. |
401 | 11.6k | if (sizeof(Distance)==4 && DBits>=30) |
402 | 0 | Distance=(size_t)-1; |
403 | 11.6k | } |
404 | 10.8k | else |
405 | 10.8k | { |
406 | 10.8k | Distance+=D.Inp.getbits()>>(16-DBits); |
407 | 10.8k | D.Inp.addbits(DBits); |
408 | 10.8k | } |
409 | 22.5k | } |
410 | | |
411 | 89.9k | if (Distance>0x100) |
412 | 5.43k | { |
413 | 5.43k | Length++; |
414 | 5.43k | if (Distance>0x2000) |
415 | 3.62k | { |
416 | 3.62k | Length++; |
417 | 3.62k | if (Distance>0x40000) |
418 | 2.62k | Length++; |
419 | 3.62k | } |
420 | 5.43k | } |
421 | | |
422 | 89.9k | CurItem->Type=UNPDT_MATCH; |
423 | 89.9k | CurItem->Length=(ushort)Length; |
424 | 89.9k | CurItem->Distance=Distance; |
425 | 89.9k | continue; |
426 | 89.9k | } |
427 | 302k | if (MainSlot==256) |
428 | 13.4k | { |
429 | 13.4k | UnpackFilter Filter; |
430 | 13.4k | ReadFilter(D.Inp,Filter); |
431 | | |
432 | 13.4k | CurItem->Type=UNPDT_FILTER; |
433 | 13.4k | CurItem->Length=Filter.Type; |
434 | 13.4k | CurItem->Distance=Filter.BlockStart; |
435 | | |
436 | 13.4k | CurItem=D.Decoded+D.DecodedSize++; |
437 | | |
438 | 13.4k | CurItem->Type=UNPDT_FILTER; |
439 | 13.4k | CurItem->Length=Filter.Channels; |
440 | 13.4k | CurItem->Distance=Filter.BlockLength; |
441 | | |
442 | 13.4k | continue; |
443 | 13.4k | } |
444 | 288k | if (MainSlot==257) |
445 | 283k | { |
446 | 283k | CurItem->Type=UNPDT_FULLREP; |
447 | 283k | continue; |
448 | 283k | } |
449 | 4.57k | if (MainSlot<262) |
450 | 4.57k | { |
451 | 4.57k | CurItem->Type=UNPDT_REP; |
452 | 4.57k | CurItem->Distance=MainSlot-258; |
453 | 4.57k | uint LengthSlot=DecodeNumber(D.Inp,&D.BlockTables.RD); |
454 | 4.57k | uint Length=SlotToLength(D.Inp,LengthSlot); |
455 | 4.57k | CurItem->Length=(ushort)Length; |
456 | 4.57k | continue; |
457 | 4.57k | } |
458 | 4.57k | } |
459 | 1.17k | } |
460 | | |
461 | | |
462 | | // Process decoded Huffman block data. |
463 | | bool Unpack::ProcessDecoded(UnpackThreadData &D) |
464 | 1.19k | { |
465 | 1.19k | UnpackDecodedItem *Item=D.Decoded,*Border=D.Decoded+D.DecodedSize; |
466 | 796k | while (Item<Border) |
467 | 795k | { |
468 | 795k | UnpPtr=WrapUp(UnpPtr); |
469 | | |
470 | 795k | FirstWinDone|=(PrevPtr>UnpPtr); |
471 | 795k | PrevPtr=UnpPtr; |
472 | | |
473 | 795k | if (WrapDown(WriteBorder-UnpPtr)<=MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr) |
474 | 2.40k | { |
475 | 2.40k | UnpWriteBuf(); |
476 | 2.40k | if (WrittenFileSize>DestUnpSize) |
477 | 25 | return false; |
478 | 2.40k | } |
479 | | |
480 | 795k | if (Item->Type==UNPDT_LITERAL) |
481 | 405k | { |
482 | 405k | #if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED) |
483 | 405k | if (Item->Length==7 && UnpPtr<MaxWinSize-8) |
484 | 375k | { |
485 | 375k | *(uint64 *)(Window+UnpPtr)=*(uint64 *)(Item->Literal); |
486 | 375k | UnpPtr+=8; |
487 | 375k | } |
488 | 30.8k | else |
489 | 30.8k | #endif |
490 | 78.4k | for (uint I=0;I<=Item->Length;I++) |
491 | 47.5k | Window[WrapUp(UnpPtr++)]=Item->Literal[I]; |
492 | 405k | } |
493 | 389k | else |
494 | 389k | if (Item->Type==UNPDT_MATCH) |
495 | 88.1k | { |
496 | 88.1k | InsertOldDist(Item->Distance); |
497 | 88.1k | LastLength=Item->Length; |
498 | 88.1k | CopyString(Item->Length,Item->Distance); |
499 | 88.1k | } |
500 | 301k | else |
501 | 301k | if (Item->Type==UNPDT_REP) |
502 | 4.57k | { |
503 | 4.57k | size_t Distance=OldDist[Item->Distance]; |
504 | 12.7k | for (size_t I=Item->Distance;I>0;I--) |
505 | 8.16k | OldDist[I]=OldDist[I-1]; |
506 | 4.57k | OldDist[0]=Distance; |
507 | 4.57k | LastLength=Item->Length; |
508 | 4.57k | CopyString(Item->Length,Distance); |
509 | 4.57k | } |
510 | 297k | else |
511 | 297k | if (Item->Type==UNPDT_FULLREP) |
512 | 283k | { |
513 | 283k | if (LastLength!=0) |
514 | 271k | CopyString(LastLength,OldDist[0]); |
515 | 283k | } |
516 | 13.3k | else |
517 | 13.3k | if (Item->Type==UNPDT_FILTER) |
518 | 13.3k | { |
519 | 13.3k | UnpackFilter Filter; |
520 | | |
521 | 13.3k | Filter.Type=(byte)Item->Length; |
522 | 13.3k | Filter.BlockStart=Item->Distance; |
523 | | |
524 | 13.3k | Item++; |
525 | | |
526 | 13.3k | Filter.Channels=(byte)Item->Length; |
527 | 13.3k | Filter.BlockLength=(uint)Item->Distance; |
528 | | |
529 | 13.3k | AddFilter(Filter); |
530 | 13.3k | } |
531 | 795k | Item++; |
532 | 795k | } |
533 | 1.16k | return true; |
534 | 1.19k | } |
535 | | |
536 | | |
537 | | // For large blocks we decode and process in same function in single threaded |
538 | | // mode, so we do not need to store intermediate data in memory. |
539 | | bool Unpack::UnpackLargeBlock(UnpackThreadData &D) |
540 | 1.50k | { |
541 | 1.50k | if (!D.TableRead) |
542 | 1.10k | { |
543 | 1.10k | D.TableRead=true; |
544 | 1.10k | if (!ReadTables(D.Inp,D.BlockHeader,D.BlockTables)) |
545 | 1 | { |
546 | 1 | D.DamagedData=true; |
547 | 1 | return false; |
548 | 1 | } |
549 | 1.10k | } |
550 | | |
551 | 1.50k | if (D.Inp.InAddr>D.BlockHeader.HeaderSize+D.BlockHeader.BlockSize) |
552 | 0 | { |
553 | 0 | D.DamagedData=true; |
554 | 0 | return false; |
555 | 0 | } |
556 | | |
557 | 1.50k | int BlockBorder=D.BlockHeader.BlockStart+D.BlockHeader.BlockSize-1; |
558 | | |
559 | | // Reserve enough space even for filter entry. |
560 | 1.50k | int DataBorder=D.DataSize-16; |
561 | 1.50k | int ReadBorder=Min(BlockBorder,DataBorder); |
562 | | |
563 | 7.03M | while (true) |
564 | 7.03M | { |
565 | 7.03M | UnpPtr=WrapUp(UnpPtr); |
566 | | |
567 | 7.03M | FirstWinDone|=(PrevPtr>UnpPtr); |
568 | 7.03M | PrevPtr=UnpPtr; |
569 | | |
570 | 7.03M | if (D.Inp.InAddr>=ReadBorder) |
571 | 13.0k | { |
572 | 13.0k | if (D.Inp.InAddr>BlockBorder || D.Inp.InAddr==BlockBorder && |
573 | 13.0k | D.Inp.InBit>=D.BlockHeader.BlockBitSize) |
574 | 0 | break; |
575 | | |
576 | | // If we do not have any more data in file to read, we must process |
577 | | // what we have until last byte. Otherwise we can return and append |
578 | | // more data to unprocessed few bytes. |
579 | 13.0k | if ((D.Inp.InAddr>=DataBorder) && !D.NoDataLeft || D.Inp.InAddr>=D.DataSize) |
580 | 1.46k | { |
581 | 1.46k | D.Incomplete=true; |
582 | 1.46k | break; |
583 | 1.46k | } |
584 | 13.0k | } |
585 | 7.03M | if (WrapDown(WriteBorder-UnpPtr)<=MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr) |
586 | 3.02k | { |
587 | 3.02k | UnpWriteBuf(); |
588 | 3.02k | if (WrittenFileSize>DestUnpSize) |
589 | 38 | return false; |
590 | 3.02k | } |
591 | | |
592 | 7.03M | uint MainSlot=DecodeNumber(D.Inp,&D.BlockTables.LD); |
593 | 7.03M | if (MainSlot<256) |
594 | 4.86M | { |
595 | 4.86M | Window[UnpPtr++]=(byte)MainSlot; |
596 | 4.86M | continue; |
597 | 4.86M | } |
598 | 2.16M | if (MainSlot>=262) |
599 | 1.48M | { |
600 | 1.48M | uint Length=SlotToLength(D.Inp,MainSlot-262); |
601 | | |
602 | 1.48M | size_t Distance=1; |
603 | 1.48M | uint DBits,DistSlot=DecodeNumber(D.Inp,&D.BlockTables.DD); |
604 | 1.48M | if (DistSlot<4) |
605 | 879k | { |
606 | 879k | DBits=0; |
607 | 879k | Distance+=DistSlot; |
608 | 879k | } |
609 | 603k | else |
610 | 603k | { |
611 | 603k | DBits=DistSlot/2 - 1; |
612 | 603k | Distance+=size_t(2 | (DistSlot & 1)) << DBits; |
613 | 603k | } |
614 | | |
615 | 1.48M | if (DBits>0) |
616 | 603k | { |
617 | 603k | if (DBits>=4) |
618 | 470k | { |
619 | 470k | if (DBits>4) |
620 | 284k | { |
621 | | // It is also possible to always use getbits64() here. |
622 | 284k | if (DBits>36) |
623 | 8.14k | Distance+=( ( size_t(D.Inp.getbits64() ) >> (68-DBits) ) << 4 ); |
624 | 276k | else |
625 | 276k | Distance+=( ( size_t(D.Inp.getbits32() ) >> (36-DBits) ) << 4 ); |
626 | 284k | D.Inp.addbits(DBits-4); |
627 | 284k | } |
628 | 470k | uint LowDist=DecodeNumber(D.Inp,&D.BlockTables.LDD); |
629 | 470k | Distance+=LowDist; |
630 | | |
631 | | // Distance can be 0 for multiples of 4 GB as result of size_t |
632 | | // overflow in 32-bit build. Its lower 32-bit can also erroneously |
633 | | // fit into dictionary after truncating upper 32-bits. Replace such |
634 | | // invalid distances with -1, so CopyString sets 0 data for them. |
635 | | // DBits>=30 also as DistSlot>=62 indicate distances >=0x80000001. |
636 | 470k | if (sizeof(Distance)==4 && DBits>=30) |
637 | 0 | Distance=(size_t)-1; |
638 | 470k | } |
639 | 133k | else |
640 | 133k | { |
641 | 133k | Distance+=D.Inp.getbits32()>>(32-DBits); |
642 | 133k | D.Inp.addbits(DBits); |
643 | 133k | } |
644 | 603k | } |
645 | | |
646 | 1.48M | if (Distance>0x100) |
647 | 275k | { |
648 | 275k | Length++; |
649 | 275k | if (Distance>0x2000) |
650 | 237k | { |
651 | 237k | Length++; |
652 | 237k | if (Distance>0x40000) |
653 | 113k | Length++; |
654 | 237k | } |
655 | 275k | } |
656 | | |
657 | 1.48M | InsertOldDist(Distance); |
658 | 1.48M | LastLength=Length; |
659 | 1.48M | CopyString(Length,Distance); |
660 | 1.48M | continue; |
661 | 1.48M | } |
662 | 678k | if (MainSlot==256) |
663 | 435k | { |
664 | 435k | UnpackFilter Filter; |
665 | 435k | if (!ReadFilter(D.Inp,Filter) || !AddFilter(Filter)) |
666 | 0 | break; |
667 | 435k | continue; |
668 | 435k | } |
669 | 243k | if (MainSlot==257) |
670 | 100k | { |
671 | 100k | if (LastLength!=0) |
672 | 15.3k | CopyString(LastLength,OldDist[0]); |
673 | 100k | continue; |
674 | 100k | } |
675 | 142k | if (MainSlot<262) |
676 | 142k | { |
677 | 142k | uint DistNum=MainSlot-258; |
678 | 142k | size_t Distance=OldDist[DistNum]; |
679 | 463k | for (uint I=DistNum;I>0;I--) |
680 | 320k | OldDist[I]=OldDist[I-1]; |
681 | 142k | OldDist[0]=Distance; |
682 | | |
683 | 142k | uint LengthSlot=DecodeNumber(D.Inp,&D.BlockTables.RD); |
684 | 142k | uint Length=SlotToLength(D.Inp,LengthSlot); |
685 | 142k | LastLength=Length; |
686 | 142k | CopyString(Length,Distance); |
687 | 142k | continue; |
688 | 142k | } |
689 | 142k | } |
690 | 1.46k | return true; |
691 | 1.50k | } |