FragmentedWindow()1 FragmentedWindow::FragmentedWindow()
2 {
3   memset(Mem,0,sizeof(Mem));
4   memset(MemSize,0,sizeof(MemSize));
5 }
6 
7 
~FragmentedWindow()8 FragmentedWindow::~FragmentedWindow()
9 {
10   Reset();
11 }
12 
13 
Reset()14 void FragmentedWindow::Reset()
15 {
16   for (uint I=0;I<ASIZE(Mem);I++)
17     if (Mem[I]!=NULL)
18     {
19       free(Mem[I]);
20       Mem[I]=NULL;
21     }
22 }
23 
24 
Init(size_t WinSize)25 void FragmentedWindow::Init(size_t WinSize)
26 {
27   Reset();
28 
29   uint BlockNum=0;
30   size_t TotalSize=0; // Already allocated.
31   while (TotalSize<WinSize && BlockNum<ASIZE(Mem))
32   {
33     size_t Size=WinSize-TotalSize; // Size needed to allocate.
34 
35     // Minimum still acceptable block size. Next allocations cannot be larger
36     // than current, so we do not need blocks if they are smaller than
37     // "size left / attempts left". Also we do not waste time to blocks
38     // smaller than some arbitrary constant.
39     size_t MinSize=Max(Size/(ASIZE(Mem)-BlockNum), 0x400000);
40 
41     byte *NewMem=NULL;
42     while (Size>=MinSize)
43     {
44       NewMem=(byte *)malloc(Size);
45       if (NewMem!=NULL)
46         break;
47       Size-=Size/32;
48     }
49     if (NewMem==NULL)
50       throw std::bad_alloc();
51 
52     // Clean the window to generate the same output when unpacking corrupt
53     // RAR files, which may access to unused areas of sliding dictionary.
54     memset(NewMem,0,Size);
55 
56     Mem[BlockNum]=NewMem;
57     TotalSize+=Size;
58     MemSize[BlockNum]=TotalSize;
59     BlockNum++;
60   }
61   if (TotalSize<WinSize) // Not found enough free blocks.
62     throw std::bad_alloc();
63 }
64 
65 
operator [](size_t Item)66 byte& FragmentedWindow::operator [](size_t Item)
67 {
68   if (Item<MemSize[0])
69     return Mem[0][Item];
70   for (uint I=1;I<ASIZE(MemSize);I++)
71     if (Item<MemSize[I])
72       return Mem[I][Item-MemSize[I-1]];
73   return Mem[0][0]; // Must never happen;
74 }
75 
76 
CopyString(uint Length,uint Distance,size_t & UnpPtr,size_t MaxWinMask)77 void FragmentedWindow::CopyString(uint Length,uint Distance,size_t &UnpPtr,size_t MaxWinMask)
78 {
79   size_t SrcPtr=UnpPtr-Distance;
80   while (Length-- > 0)
81   {
82     (*this)[UnpPtr]=(*this)[SrcPtr++ & MaxWinMask];
83     // We need to have masked UnpPtr after quit from loop, so it must not
84     // be replaced with '(*this)[UnpPtr++ & MaxWinMask]'
85     UnpPtr=(UnpPtr+1) & MaxWinMask;
86   }
87 }
88 
89 
CopyData(byte * Dest,size_t WinPos,size_t Size)90 void FragmentedWindow::CopyData(byte *Dest,size_t WinPos,size_t Size)
91 {
92   for (size_t I=0;I<Size;I++)
93     Dest[I]=(*this)[WinPos+I];
94 }
95 
96 
GetBlockSize(size_t StartPos,size_t RequiredSize)97 size_t FragmentedWindow::GetBlockSize(size_t StartPos,size_t RequiredSize)
98 {
99   for (uint I=0;I<ASIZE(MemSize);I++)
100     if (StartPos<MemSize[I])
101       return Min(MemSize[I]-StartPos,RequiredSize);
102   return 0; // Must never be here.
103 }
104