libclamunrar/suballoc.cpp
d39cb658
 /****************************************************************************
  *  This file is part of PPMd project                                       *
  *  Written and distributed to public domain by Dmitry Shkarin 1997,        *
  *  1999-2000                                                               *
  *  Contents: memory allocation routines                                    *
  ****************************************************************************/
 
 static const uint UNIT_SIZE=Max(sizeof(RARPPM_CONTEXT),sizeof(RARPPM_MEM_BLK));
 static const uint FIXED_UNIT_SIZE=12;
 
 SubAllocator::SubAllocator()
 {
   Clean();
 }
 
 
 void SubAllocator::Clean()
 {
   SubAllocatorSize=0;
 }
 
 
 inline void SubAllocator::InsertNode(void* p,int indx) 
 {
   ((RAR_NODE*) p)->next=FreeList[indx].next;
   FreeList[indx].next=(RAR_NODE*) p;
 }
 
 
 inline void* SubAllocator::RemoveNode(int indx) 
 {
   RAR_NODE* RetVal=FreeList[indx].next;
   FreeList[indx].next=RetVal->next;
   return RetVal;
 }
 
 
 inline uint SubAllocator::U2B(int NU) 
 { 
   // We calculate the size of units in bytes based on real UNIT_SIZE.
   // In original implementation it was 8*NU+4*NU.
   return UNIT_SIZE*NU;
 }
 
 
 
 // Calculate RARPPM_MEM_BLK+Items address. Real RARPPM_MEM_BLK size must be
 // equal to UNIT_SIZE, so we cannot just add Items to RARPPM_MEM_BLK address.
 inline RARPPM_MEM_BLK* SubAllocator::MBPtr(RARPPM_MEM_BLK *BasePtr,int Items)
 {
   return((RARPPM_MEM_BLK*)( ((byte *)(BasePtr))+U2B(Items) ));
 }
 
 
 inline void SubAllocator::SplitBlock(void* pv,int OldIndx,int NewIndx)
 {
   int i, UDiff=Indx2Units[OldIndx]-Indx2Units[NewIndx];
   byte* p=((byte*) pv)+U2B(Indx2Units[NewIndx]);
   if (Indx2Units[i=Units2Indx[UDiff-1]] != UDiff) 
   {
     InsertNode(p,--i);
     p += U2B(i=Indx2Units[i]);
     UDiff -= i;
   }
   InsertNode(p,Units2Indx[UDiff-1]);
 }
 
 
 void SubAllocator::StopSubAllocator()
 {
   if ( SubAllocatorSize ) 
   {
     SubAllocatorSize=0;
     free(HeapStart);
   }
 }
 
 
 bool SubAllocator::StartSubAllocator(int SASize)
 {
   uint t=SASize << 20;
   if (SubAllocatorSize == t)
     return true;
   StopSubAllocator();
 
   // Original algorithm expects FIXED_UNIT_SIZE, but actual structure size
   // can be larger. So let's recalculate the allocated size and add two more
   // units: one as reserve for HeapEnd overflow checks and another
   // to provide the space to correctly align UnitsStart.
   uint AllocSize=t/FIXED_UNIT_SIZE*UNIT_SIZE+2*UNIT_SIZE;
   if ((HeapStart=(byte *)malloc(AllocSize)) == NULL)
   {
     ErrHandler.MemoryError();
     return false;
   }
 
   // HeapEnd did not present in original algorithm. We added it to control
   // invalid memory access attempts when processing corrupt archived data.
   HeapEnd=HeapStart+AllocSize-UNIT_SIZE;
 
   SubAllocatorSize=t;
   return true;
 }
 
 
 void SubAllocator::InitSubAllocator()
 {
   int i, k;
   memset(FreeList,0,sizeof(FreeList));
   pText=HeapStart;
 
   // Original algorithm operates with 12 byte FIXED_UNIT_SIZE, but actual
   // size of RARPPM_MEM_BLK and RARPPM_CONTEXT structures can exceed this value
   // because of alignment and larger pointer fields size.
   // So we define UNIT_SIZE for this larger size and adjust memory
   // pointers accordingly.
 
   // Size2 is (HiUnit-LoUnit) memory area size to allocate as originally
   // supposed by compression algorithm. It is 7/8 of total allocated size.
   uint Size2=FIXED_UNIT_SIZE*(SubAllocatorSize/8/FIXED_UNIT_SIZE*7);
 
   // RealSize2 is the real adjusted size of (HiUnit-LoUnit) memory taking
   // into account that our UNIT_SIZE can be larger than FIXED_UNIT_SIZE.
   uint RealSize2=Size2/FIXED_UNIT_SIZE*UNIT_SIZE;
 
   // Size1 is the size of memory area from HeapStart to FakeUnitsStart
   // as originally supposed by compression algorithm. This area can contain
   // different data types, both single symbols and structures.
   uint Size1=SubAllocatorSize-Size2;
 
   // Real size of this area. We correct it according to UNIT_SIZE vs
   // FIXED_UNIT_SIZE difference. Also we add one more UNIT_SIZE
   // to compensate a possible reminder from Size1/FIXED_UNIT_SIZE,
   // which would be lost otherwise. We add UNIT_SIZE instead of 
   // this Size1%FIXED_UNIT_SIZE reminder, because it allows to align
   // UnitsStart easily and adding more than reminder is ok for algorithm.
   uint RealSize1=Size1/FIXED_UNIT_SIZE*UNIT_SIZE+UNIT_SIZE;
 
   // RealSize1 must be divided by UNIT_SIZE without a reminder, so UnitsStart
   // is aligned to UNIT_SIZE. It is important for those architectures,
   // where a proper memory alignment is mandatory. Since we produce RealSize1
   // multiplying by UNIT_SIZE, this condition is always true. So LoUnit,
   // UnitsStart, HeapStart are properly aligned,
   LoUnit=UnitsStart=HeapStart+RealSize1;
 
   // When we reach FakeUnitsStart, we restart the model. It is where
   // the original algorithm expected to see UnitsStart. Real UnitsStart
   // can have a larger value.
   FakeUnitsStart=HeapStart+Size1;
 
   HiUnit=LoUnit+RealSize2;
   for (i=0,k=1;i < N1     ;i++,k += 1)
     Indx2Units[i]=k;
   for (k++;i < N1+N2      ;i++,k += 2)
     Indx2Units[i]=k;
   for (k++;i < N1+N2+N3   ;i++,k += 3)
     Indx2Units[i]=k;
   for (k++;i < N1+N2+N3+N4;i++,k += 4)
     Indx2Units[i]=k;
   for (GlueCount=k=i=0;k < 128;k++)
   {
     i += (Indx2Units[i] < k+1);
     Units2Indx[k]=i;
   }
 }
 
 
 inline void SubAllocator::GlueFreeBlocks()
 {
   RARPPM_MEM_BLK s0, * p, * p1;
   int i, k, sz;
   if (LoUnit != HiUnit)
     *LoUnit=0;
   for (i=0, s0.next=s0.prev=&s0;i < N_INDEXES;i++)
     while ( FreeList[i].next )
     {
       p=(RARPPM_MEM_BLK*)RemoveNode(i);
       p->insertAt(&s0);
       p->Stamp=0xFFFF;
       p->NU=Indx2Units[i];
     }
   for (p=s0.next;p != &s0;p=p->next)
     while ((p1=MBPtr(p,p->NU))->Stamp == 0xFFFF && int(p->NU)+p1->NU < 0x10000)
     {
       p1->remove();
       p->NU += p1->NU;
     }
   while ((p=s0.next) != &s0)
   {
     for (p->remove(), sz=p->NU;sz > 128;sz -= 128, p=MBPtr(p,128))
       InsertNode(p,N_INDEXES-1);
     if (Indx2Units[i=Units2Indx[sz-1]] != sz)
     {
       k=sz-Indx2Units[--i];
       InsertNode(MBPtr(p,sz-k),k-1);
     }
     InsertNode(p,i);
   }
 }
 
 void* SubAllocator::AllocUnitsRare(int indx)
 {
   if ( !GlueCount )
   {
     GlueCount = 255;
     GlueFreeBlocks();
     if ( FreeList[indx].next )
       return RemoveNode(indx);
   }
   int i=indx;
   do
   {
     if (++i == N_INDEXES)
     {
       GlueCount--;
       i=U2B(Indx2Units[indx]);
       int j=FIXED_UNIT_SIZE*Indx2Units[indx];
       if (FakeUnitsStart - pText > j)
       {
         FakeUnitsStart -= j;
         UnitsStart -= i;
         return UnitsStart;
       }
       return NULL;
     }
   } while ( !FreeList[i].next );
   void* RetVal=RemoveNode(i);
   SplitBlock(RetVal,i,indx);
   return RetVal;
 }
 
 
 inline void* SubAllocator::AllocUnits(int NU)
 {
   int indx=Units2Indx[NU-1];
   if ( FreeList[indx].next )
     return RemoveNode(indx);
   void* RetVal=LoUnit;
   LoUnit += U2B(Indx2Units[indx]);
   if (LoUnit <= HiUnit)
     return RetVal;
   LoUnit -= U2B(Indx2Units[indx]);
   return AllocUnitsRare(indx);
 }
 
 
 void* SubAllocator::AllocContext()
 {
   if (HiUnit != LoUnit)
     return (HiUnit -= UNIT_SIZE);
   if ( FreeList->next )
     return RemoveNode(0);
   return AllocUnitsRare(0);
 }
 
 
 void* SubAllocator::ExpandUnits(void* OldPtr,int OldNU)
 {
   int i0=Units2Indx[OldNU-1], i1=Units2Indx[OldNU-1+1];
   if (i0 == i1)
     return OldPtr;
   void* ptr=AllocUnits(OldNU+1);
   if ( ptr ) 
   {
     memcpy(ptr,OldPtr,U2B(OldNU));
     InsertNode(OldPtr,i0);
   }
   return ptr;
 }
 
 
 void* SubAllocator::ShrinkUnits(void* OldPtr,int OldNU,int NewNU)
 {
   int i0=Units2Indx[OldNU-1], i1=Units2Indx[NewNU-1];
   if (i0 == i1)
     return OldPtr;
   if ( FreeList[i1].next )
   {
     void* ptr=RemoveNode(i1);
     memcpy(ptr,OldPtr,U2B(NewNU));
     InsertNode(OldPtr,i0);
     return ptr;
   } 
   else 
   {
     SplitBlock(OldPtr,i0,i1);
     return OldPtr;
   }
 }
 
 
 void SubAllocator::FreeUnits(void* ptr,int OldNU)
 {
   InsertNode(ptr,Units2Indx[OldNU-1]);
 }