/*
This product contains certain software code or other information
("AT&T Software") proprietary to AT&T Corp. ("AT&T").  The AT&T
Software is provided to you "AS IS".  YOU ASSUME TOTAL RESPONSIBILITY
AND RISK FOR USE OF THE AT&T SOFTWARE.  AT&T DOES NOT MAKE, AND
EXPRESSLY DISCLAIMS, ANY EXPRESS OR IMPLIED WARRANTIES OF ANY KIND
WHATSOEVER, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, WARRANTIES OF
TITLE OR NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS, ANY
WARRANTIES ARISING BY USAGE OF TRADE, COURSE OF DEALING OR COURSE OF
PERFORMANCE, OR ANY WARRANTY THAT THE AT&T SOFTWARE IS "ERROR FREE" OR
WILL MEET YOUR REQUIREMENTS.

Unless you accept a license to use the AT&T Software, you shall not
reverse compile, disassemble or otherwise reverse engineer this
product to ascertain the source code for any AT&T Software.

(c) AT&T Corp. All rights reserved.  AT&T is a registered trademark of AT&T Corp.

***********************************************************************

History:

      24/11/99  - initial release by Hartmut Liefke, liefke@seas.upenn.edu
                                     Dan Suciu,      suciu@research.att.com
*/

//**************************************************************************
//**************************************************************************

// This module contains the container manager for the decompressor

#include "stdafx.h"

/* forward references */
class VPathExpr;
class Input;
class UserUncompressor;

UncompressContainer::UncompressContainer(Session *s)
{
	SetSession(s);
}

void UncompressContainer::SetSession(Session *s)
{
	session = s;
}

/* class UncompressContainer implementation */
inline void UncompressContainer::AllocateContMem(unsigned long mincontsize)
   // Allocates the memory - but only if the size is larger than 'mincontsize'
   // This will allows us to allocate memory starting with the largest
   // containers. Then, smaller containers can be allocated.
{
   if((dataptr!=NULL)||(size<mincontsize))
      return;

   curptr=dataptr=session->AllocateMemBlock(size);
}

void UncompressContainer::UncompressSmallContainer(SmallBlockUncompressor *uncompressor)
   // Decompresses the small container data and stores
   // it in the data buffer
{
   unsigned char *srcptr=uncompressor->LoadData(size);
   memcpy(dataptr,srcptr,size);
}

void UncompressContainer::UncompressLargeContainer(Input *input)
   // Decompresses the large container data and stores
   // it in the data buffer
{
   Uncompressor uncompress(session);
   unsigned long  uncompsize=size, last = 0;
   char dataleft = TRUE;
      
   while (dataleft) {
      dataleft = uncompress.Uncompress(input,&dataptr[last],&uncompsize);
      last = uncompsize;
   }
   if (uncompsize != size) {
      ExitCorruptFile();
   }
}

//****************************************************************************
//****************************************************************************
/* class UncompressContainerMan implementation */
void UncompressContainerMan::Load(SmallBlockUncompressor *uncompressor)
   // Loads the structural information from the small block decompressor
{
   // The number of container blocks
   blocknum=uncompressor->LoadUInt32();

   // Let's allocate the container block array
   blockarray=(UncompressContainerBlock *)session->blockmem->GetByteBlock(sizeof(UncompressContainerBlock)*blocknum);

   // Let's load the structural information for all container blocks
   for(unsigned i=0;i<blocknum;i++)
      blockarray[i].Load(session, uncompressor);
}

UncompressContainerMan::UncompressContainerMan(Session *s)
{
	session = s;
}

void UncompressContainerMan::AllocateContMem()
      // Allocates the memory for the container sequentially.
      // Loads the container block information (number+size+structure of container blocks)
      // from the small decompressor object
      // This function starts with large containers and then allocates
      // memory for smaller containers
{
   unsigned i;

   // We do the container allocation in 5 steps

   for(i=0;i<blocknum;i++)
      blockarray[i].AllocateContMem(1000000L);

   for(i=0;i<blocknum;i++)
      blockarray[i].AllocateContMem(200000L);

   for(i=0;i<blocknum;i++)
      blockarray[i].AllocateContMem(40000L);

   for(i=0;i<blocknum;i++)
      blockarray[i].AllocateContMem(8000L);

   for(i=0;i<blocknum;i++)
      blockarray[i].AllocateContMem(0L);
}

//****************************************************************************

/* class UncompressContainerBlock implementation */
UncompressContainerBlock::UncompressContainerBlock(Session *s)
{
	session = s;
}

inline void UncompressContainerBlock::Load(Session *s, SmallBlockUncompressor *uncompressor)
      // Loads the container block information (number+size of containers)
      // from the small decompressor object
{
   // Let's load the index of the path expression
   unsigned long pathidx=uncompressor->LoadUInt32();
	session = s;
   if(pathidx!=0)
      dpathexpr=(DecompVPathExpr*)(session->dpathexprman->GetPathExpr(pathidx-1));
   else
      dpathexpr=NULL;

   // Let's load the number of containers
   contnum=uncompressor->LoadUInt32();

   if((dpathexpr!=NULL)&&(dpathexpr->GetUserContNum()!=contnum))
   {
      ExitCorruptFile();
   }

   // Let's allocate some memory for the container structures
   // and the necessary state space
   contarray=(UncompressContainer *)session->blockmem->GetByteBlock(
      sizeof(UncompressContainer)*contnum+
      ((dpathexpr!=NULL) ? dpathexpr->GetUserDataSize() : 0));
   
   // let's load the size of each single container
	for(unsigned i=0;i<contnum;i++) {
		contarray[i].SetSession(session);
      contarray[i].SetSize(uncompressor->LoadUInt32());
	}
};

inline void UncompressContainerBlock::AllocateContMem(unsigned long mincontsize)
   // Allocates the memory for those containers with size>mincontsize
{
   for(unsigned i=0;i<contnum;i++)
      contarray[i].AllocateContMem(mincontsize);
};

void UncompressContainerBlock::UncompressSmallContainers(SmallBlockUncompressor *uncompressor)
   // Decompresses the data of small containers and stores
   // it in the data buffers
{
   unsigned long i;

   for(i=0;i<contnum;i++)
   {
      if(contarray[i].GetSize()<SMALLCONT_THRESHOLD)
         contarray[i].UncompressSmallContainer(uncompressor);
   }
}

void UncompressContainerBlock::UncompressLargeContainers(Input *input)
   // Decompresses the data of large containers and stores
   // it in the data buffers
{
   for(unsigned long i=0;i<contnum;i++)
   {
      if(contarray[i].GetSize()>=SMALLCONT_THRESHOLD)
         contarray[i].UncompressLargeContainer(input);
   }
}

//****************************************************************************
//****************************************************************************

void UncompressContainerMan::UncompressSmallContainers(SmallBlockUncompressor *uncompressor)
   // Decompresses the data of small containers and stores
   // it in the data buffers
{
   for(unsigned long i=0;i<blocknum;i++)
      blockarray[i].UncompressSmallContainers(uncompressor);
}

void UncompressContainerMan::UncompressLargeContainers(Input *input)
   // Decompresses the data of large containers and stores
   // it in the data buffers
{
   for(unsigned long i=0;i<blocknum;i++)
      blockarray[i].UncompressLargeContainers(input);
}

//****************************************************************************

void UncompressContainerMan::Init()
   // Initializes the state data for the user compressors
   // of all container blocks
{
   for(unsigned long i=0;i<blocknum;i++)
      blockarray[i].Init();
}


//****************************************************************************
//****************************************************************************

// All functions for release the memory of the containers
inline void UncompressContainer::ReleaseContMem()
{
   session->FreeMemBlock(dataptr,size);
}

inline void UncompressContainerBlock::ReleaseContMem()
{
   for(unsigned long i=0;i<contnum;i++)
      contarray[i].ReleaseContMem();
}

void UncompressContainerMan::ReleaseContMem()
{
   for(unsigned long i=0;i<blocknum;i++)
      blockarray[i].ReleaseContMem();
}

void UncompressContainerMan::FinishUncompress()
{
   for(unsigned long i=0;i<blocknum;i++)
      blockarray[i].FinishUncompress();
}

#ifdef NOTHREAD
void *UncompressContainer::operator new(size_t size)  
{  
	return ::session->blockmem->GetByteBlock(size); 
}
void UncompressContainer::operator delete(void *ptr)  {}
#endif

void UncompressContainer::SetSize(unsigned long mysize)
   // This sets the initial size - the memory is allocated later
   // and the data is loaded later
{
   dataptr=curptr=NULL;
   size=mysize;
}
unsigned long UncompressContainer::GetSize() 
{  
	return size;   
}

unsigned char *UncompressContainer::GetDataPtr()   
{  
	return curptr; 
}

unsigned char *UncompressContainer::GetDataPtr(int len)
{
   curptr+=len;
   return curptr-len;
}

// Some auxiliary functions for reading integers and strings
unsigned long UncompressContainer::LoadUInt32()
{
	return Load::UInt32(curptr);
}

unsigned long UncompressContainer::LoadSInt32(char *isneg)
{
	return Load::SInt32(curptr,isneg);
}

long UncompressContainer::LoadSInt32()
{
   char isneg;
	long val=Load::SInt32(curptr,&isneg);
   if(isneg)
      return 0L-val;
   else
      return val;
}

char UncompressContainer::LoadChar()
{
	return Load::Char(curptr);
}

unsigned char *UncompressContainer::myLoadString(unsigned *len)
{
   *len=LoadUInt32();

   curptr+=*len;
   return curptr-*len;
}

#ifdef NOTHREAD
void *UncompressContainerBlock::operator new(size_t size)  
{  
	return ::session->blockmem->GetByteBlock(size); 
}
void UncompressContainerBlock::operator delete(void *ptr)  {}
#endif

UncompressContainer  *UncompressContainerBlock::GetContainer(unsigned idx)
{  
	return contarray+idx;   
}

UserUncompressor *UncompressContainerBlock::GetUserUncompressor()
{
   return dpathexpr->GetUserUncompressor();
}

char *UncompressContainerBlock::GetUserDataPtr()
   // Returns the pointer to the user decompressor state data
{
   return (char *)(contarray+contnum);
}

void UncompressContainerBlock::Init()
   // Initializes the decompress container block
   // This initializes the decompressor state data
{
   if(dpathexpr!=NULL)
      GetUserUncompressor()->InitUncompress(GetContainer(0),GetUserDataPtr());
}

void UncompressContainerBlock::UncompressText(XMLOutput *xoutput)
   // Decompresses a text item and prints it to 'output'
{
   GetUserUncompressor()->UncompressItem(GetContainer(0),GetUserDataPtr(),xoutput);
}

void UncompressContainerBlock::FinishUncompress()
   // After all items have been read, this function cleans
   // up the user decompressor states
{
   if(dpathexpr!=NULL)
      dpathexpr->GetUserUncompressor()->FinishUncompress(GetContainer(0),GetUserDataPtr());
}

UncompressContainerBlock *UncompressContainerMan::GetContBlock(unsigned idx)   
{  
	return blockarray+idx;   
}
