blob: dee442e2d19f03fd1ec0fa78493af1d2b604c111 [file] [log] [blame]
// NOTE(vakh): The process.h file needs to be included first because "rar.hpp"
// defines certain macros that cause symbol redefinition errors
#if defined(UNRAR_NO_EXCEPTIONS)
#include "base/process/memory.h"
#endif // defined(UNRAR_NO_EXCEPTIONS)
#include "rar.hpp"
#include "coder.cpp"
#include "suballoc.cpp"
#include "model.cpp"
#include "unpackinline.cpp"
#ifdef RAR_SMP
#include "unpack50mt.cpp"
#endif
#ifndef SFX_MODULE
#include "unpack15.cpp"
#include "unpack20.cpp"
#endif
#include "unpack30.cpp"
#include "unpack50.cpp"
#include "unpack50frag.cpp"
namespace third_party_unrar {
Unpack::Unpack(ComprDataIO *DataIO)
:Inp(true),VMCodeInp(true)
{
UnpIO=DataIO;
Window=NULL;
Fragmented=false;
Suspended=false;
UnpAllBuf=false;
UnpSomeRead=false;
#ifdef RAR_SMP
MaxUserThreads=1;
UnpThreadPool=CreateThreadPool();
ReadBufMT=NULL;
UnpThreadData=NULL;
#endif
MaxWinSize=0;
MaxWinMask=0;
// Perform initialization, which should be done only once for all files.
// It prevents crash if first DoUnpack call is later made with wrong
// (true) 'Solid' value.
UnpInitData(false);
#ifndef SFX_MODULE
// RAR 1.5 decompression initialization
UnpInitData15(false);
InitHuff();
#endif
}
Unpack::~Unpack()
{
InitFilters30(false);
if (Window!=NULL)
free(Window);
#ifdef RAR_SMP
DestroyThreadPool(UnpThreadPool);
delete[] ReadBufMT;
delete[] UnpThreadData;
#endif
}
void Unpack::Init(size_t WinSize,bool Solid)
{
// If 32-bit RAR unpacks an archive with 4 GB dictionary, the window size
// will be 0 because of size_t overflow. Let's issue the memory error.
if (WinSize==0)
ErrHandler.MemoryError();
// Minimum window size must be at least twice more than maximum possible
// size of filter block, which is 0x10000 in RAR now. If window size is
// smaller, we can have a block with never cleared flt->NextWindow flag
// in UnpWriteBuf(). Minimum window size 0x20000 would be enough, but let's
// use 0x40000 for extra safety and possible filter area size expansion.
const size_t MinAllocSize=0x40000;
if (WinSize<MinAllocSize)
WinSize=MinAllocSize;
if (WinSize<=MaxWinSize) // Use the already allocated window.
return;
if ((WinSize>>16)>0x10000) // Window size must not exceed 4 GB.
return;
// Archiving code guarantees that window size does not grow in the same
// solid stream. So if we are here, we are either creating a new window
// or increasing the size of non-solid window. So we could safely reject
// current window data without copying them to a new window, though being
// extra cautious, we still handle the solid window grow case below.
bool Grow=Solid && (Window!=NULL || Fragmented);
// We do not handle growth for existing fragmented window.
if (Grow && Fragmented)
{
#if defined(UNRAR_NO_EXCEPTIONS)
base::TerminateBecauseOutOfMemory(0);
#else
throw std::bad_alloc();
#endif // defined(UNRAR_NO_EXCEPTIONS)
}
byte *NewWindow=Fragmented ? NULL : (byte *)malloc(WinSize);
if (NewWindow==NULL)
{
if (Grow || WinSize<0x1000000)
{
// We do not support growth for new fragmented window.
// Also exclude RAR4 and small dictionaries.
#if defined(UNRAR_NO_EXCEPTIONS)
base::TerminateBecauseOutOfMemory(WinSize);
#else
throw std::bad_alloc();
#endif // defined(UNRAR_NO_EXCEPTIONS)
}
else
{
if (Window!=NULL) // If allocated by preceding files.
{
free(Window);
Window=NULL;
}
FragWindow.Init(WinSize);
Fragmented=true;
}
}
if (!Fragmented)
{
// Clean the window to generate the same output when unpacking corrupt
// RAR files, which may access unused areas of sliding dictionary.
memset(NewWindow,0,WinSize);
// If Window is not NULL, it means that window size has grown.
// In solid streams we need to copy data to a new window in such case.
// RAR archiving code does not allow it in solid streams now,
// but let's implement it anyway just in case we'll change it sometimes.
if (Grow)
for (size_t I=1;I<=MaxWinSize;I++)
NewWindow[(UnpPtr-I)&(WinSize-1)]=Window[(UnpPtr-I)&(MaxWinSize-1)];
if (Window!=NULL)
free(Window);
Window=NewWindow;
}
MaxWinSize=WinSize;
MaxWinMask=MaxWinSize-1;
}
void Unpack::DoUnpack(uint Method,bool Solid)
{
// Methods <50 will crash in Fragmented mode when accessing NULL Window.
// They cannot be called in such mode now, but we check it below anyway
// just for extra safety.
switch(Method)
{
#ifndef SFX_MODULE
case 15: // rar 1.5 compression
if (!Fragmented)
Unpack15(Solid);
break;
case 20: // rar 2.x compression
case 26: // files larger than 2GB
if (!Fragmented)
Unpack20(Solid);
break;
#endif
case 29: // rar 3.x compression
if (!Fragmented)
Unpack29(Solid);
break;
case 50: // RAR 5.0 compression algorithm.
#ifdef RAR_SMP
if (MaxUserThreads>1)
{
// We do not use the multithreaded unpack routine to repack RAR archives
// in 'suspended' mode, because unlike the single threaded code it can
// write more than one dictionary for same loop pass. So we would need
// larger buffers of unknown size. Also we do not support multithreading
// in fragmented window mode.
if (!Fragmented)
{
Unpack5MT(Solid);
break;
}
}
#endif
Unpack5(Solid);
break;
}
}
void Unpack::UnpInitData(bool Solid)
{
if (!Solid)
{
memset(OldDist,0,sizeof(OldDist));
OldDistPtr=0;
LastDist=LastLength=0;
// memset(Window,0,MaxWinSize);
memset(&BlockTables,0,sizeof(BlockTables));
UnpPtr=WrPtr=0;
WriteBorder=Min(MaxWinSize,UNPACK_MAX_WRITE)&MaxWinMask;
}
// Filters never share several solid files, so we can safely reset them
// even in solid archive.
InitFilters();
Inp.InitBitInput();
WrittenFileSize=0;
ReadTop=0;
ReadBorder=0;
memset(&BlockHeader,0,sizeof(BlockHeader));
BlockHeader.BlockSize=-1; // '-1' means not defined yet.
#ifndef SFX_MODULE
UnpInitData20(Solid);
#endif
UnpInitData30(Solid);
UnpInitData50(Solid);
}
// LengthTable contains the length in bits for every element of alphabet.
// Dec is the structure to decode Huffman code/
// Size is size of length table and DecodeNum field in Dec structure,
void Unpack::MakeDecodeTables(byte *LengthTable,DecodeTable *Dec,uint Size)
{
// Size of alphabet and DecodePos array.
Dec->MaxNum=Size;
// Calculate how many entries for every bit length in LengthTable we have.
uint LengthCount[16];
memset(LengthCount,0,sizeof(LengthCount));
for (size_t I=0;I<Size;I++)
LengthCount[LengthTable[I] & 0xf]++;
// We must not calculate the number of zero length codes.
LengthCount[0]=0;
// Set the entire DecodeNum to zero.
memset(Dec->DecodeNum,0,Size*sizeof(*Dec->DecodeNum));
// Initialize not really used entry for zero length code.
Dec->DecodePos[0]=0;
// Start code for bit length 1 is 0.
Dec->DecodeLen[0]=0;
// Right aligned upper limit code for current bit length.
uint UpperLimit=0;
for (size_t I=1;I<16;I++)
{
// Adjust the upper limit code.
UpperLimit+=LengthCount[I];
// Left aligned upper limit code.
uint LeftAligned=UpperLimit<<(16-I);
// Prepare the upper limit code for next bit length.
UpperLimit*=2;
// Store the left aligned upper limit code.
Dec->DecodeLen[I]=(uint)LeftAligned;
// Every item of this array contains the sum of all preceding items.
// So it contains the start position in code list for every bit length.
Dec->DecodePos[I]=Dec->DecodePos[I-1]+LengthCount[I-1];
}
// Prepare the copy of DecodePos. We'll modify this copy below,
// so we cannot use the original DecodePos.
uint CopyDecodePos[ASIZE(Dec->DecodePos)];
memcpy(CopyDecodePos,Dec->DecodePos,sizeof(CopyDecodePos));
// For every bit length in the bit length table and so for every item
// of alphabet.
for (uint I=0;I<Size;I++)
{
// Get the current bit length.
byte CurBitLength=LengthTable[I] & 0xf;
if (CurBitLength!=0)
{
// Last position in code list for current bit length.
uint LastPos=CopyDecodePos[CurBitLength];
// Prepare the decode table, so this position in code list will be
// decoded to current alphabet item number.
Dec->DecodeNum[LastPos]=(ushort)I;
// We'll use next position number for this bit length next time.
// So we pass through the entire range of positions available
// for every bit length.
CopyDecodePos[CurBitLength]++;
}
}
// Define the number of bits to process in quick mode. We use more bits
// for larger alphabets. More bits means that more codes will be processed
// in quick mode, but also that more time will be spent to preparation
// of tables for quick decode.
switch (Size)
{
case NC:
case NC20:
case NC30:
Dec->QuickBits=MAX_QUICK_DECODE_BITS;
break;
default:
Dec->QuickBits=MAX_QUICK_DECODE_BITS-3;
break;
}
// Size of tables for quick mode.
uint QuickDataSize=1<<Dec->QuickBits;
// Bit length for current code, start from 1 bit codes. It is important
// to use 1 bit instead of 0 for minimum code length, so we are moving
// forward even when processing a corrupt archive.
uint CurBitLength=1;
// For every right aligned bit string which supports the quick decoding.
for (uint Code=0;Code<QuickDataSize;Code++)
{
// Left align the current code, so it will be in usual bit field format.
uint BitField=Code<<(16-Dec->QuickBits);
// Prepare the table for quick decoding of bit lengths.
// Find the upper limit for current bit field and adjust the bit length
// accordingly if necessary.
while (CurBitLength<ASIZE(Dec->DecodeLen) && BitField>=Dec->DecodeLen[CurBitLength])
CurBitLength++;
// Translation of right aligned bit string to bit length.
Dec->QuickLen[Code]=CurBitLength;
// Prepare the table for quick translation of position in code list
// to position in alphabet.
// Calculate the distance from the start code for current bit length.
uint Dist=BitField-Dec->DecodeLen[CurBitLength-1];
// Right align the distance.
Dist>>=(16-CurBitLength);
// Now we can calculate the position in the code list. It is the sum
// of first position for current bit length and right aligned distance
// between our bit field and start code for current bit length.
uint Pos;
if (CurBitLength<ASIZE(Dec->DecodePos) &&
(Pos=Dec->DecodePos[CurBitLength]+Dist)<Size)
{
// Define the code to alphabet number translation.
Dec->QuickNum[Code]=Dec->DecodeNum[Pos];
}
else
{
// Can be here for length table filled with zeroes only (empty).
Dec->QuickNum[Code]=0;
}
}
}
} // namespace third_party_unrar