/*
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 label dictionary manager
// The label dictionary stores the associations between label IDs and
// the actual label name.

#include "stdafx.h"

char CompressLabelDictItem::IsAttrib()   
{  
	return ISATTRIB(labelid);  
}

char *CompressLabelDictItem::GetLabelPtr()  
	// Returns the pointer to the label name
{  
	return (char *)(this+1); 
}

unsigned short CompressLabelDictItem::GetLabelLen()  
	// Returns the label length
{  
	return len; 
}

/* general methods */
LabelDict::LabelDict(Session *s)
{
	session = s;
	initialized = false;
}

void LabelDict::PrintLabel(TLabelID labelid)
   // Prints the label with labelid 'labelid'
{
   char           *ptr;
   unsigned long  len;
   unsigned char  isattrib=ISATTRIB(labelid);

   labelid=(labelid&(ATTRIBLABEL_STARTIDX-1));

	/* call virtual function */
	len=LookupCompressLabel(labelid,&ptr,&isattrib);

   if(isattrib)   // Attribute names start with '@'
   {
      printf("@");
      fwrite(ptr,len,1,stdout);
   }
   else
      fwrite(ptr,len,1,stdout);
}

void LabelDict::Print()
   // Prints all labels in the dictionary
{
   for(unsigned long i=0;i<labelnum;i++)
   {
      printf("%lu : ",i);
      PrintLabel((TLabelID)i);
      printf("\n");
   }
}

#ifdef PROFILE
void LabelDict::PrintProfile()
{
   printf("Labeldict: count=%lu lookupcount=%lu   hashitercount=%lu\n",labelnum,lookupcount,hashitercount);
}
#endif

void LabelDict::Init()
{
	if (!initialized) {
#ifdef PROFILE
      lookupcount=0;
      hashitercount=0;
#endif
      labelnum=0;
		InitMore();
		initialized = true;
	}
}

// ************** These are methods for the compressor ****************************

CompLabelDict::CompLabelDict(Session *s): LabelDict(s)
{
	hashtable = NULL;
}
CompLabelDict::~CompLabelDict() 
{
	trydel (hashtable);
}

void CompLabelDict::InitMore()
{
      // No labels
      labels=NULL;
      labelref=&labels;

      // no saved labels until now
      savedlabelnum=0;
      savedlabelref=&labels;

		if (!hashtable) {
			// let's get some memory for the hash table
	//      session->mainmem->WordAlign();
	//      hashtable=(CompressLabelDictItem **)session->mainmem->GetByteBlock(sizeof(CompressLabelDictItem *)*HASHTABLE_SIZE);
			hashtable=(CompressLabelDictItem **)new char[sizeof(CompressLabelDictItem *)*HASHTABLE_SIZE];
			if(hashtable==NULL)
				ExitNoMem();

			for(int i=0;i<HASHTABLE_SIZE;i++)
				hashtable[i]=NULL;
		}
}

void CompLabelDict::Reset()
{
		int      i;
      TLabelID labelid;
      CompressLabelDictItem **hashtableref;

      // First, let's scan over the hash table to remove all
      // entries of not predefined labels
      for(i=0;i<HASHTABLE_SIZE;i++)
      {
         hashtableref=hashtable+i;
         while(*hashtableref!=NULL)
         {
            if((*hashtableref)->labelid>=predefinedlabelnum)
               // Not a predefined label ID?
               // ==> Delete
               *hashtableref=(*hashtableref)->nextsamehash;
            else
               // Otherwise, we go to the next label
               hashtableref=&((*hashtableref)->nextsamehash);
         }
      }

      // Now we need to cut the global list of labels.
      // We keep the first 'predefinedlabelnum' predefined labels.
      CompressLabelDictItem **curlabelref=&labels;

      for(labelid=0;labelid<predefinedlabelnum;labelid++)
         curlabelref=&((*curlabelref)->next);

      *curlabelref=NULL;

      labelref=curlabelref;
      labelnum=predefinedlabelnum;

      // no saved labels until now
      savedlabelnum=0;
      savedlabelref=&labels;
}

void CompLabelDict::FinishedPredefinedLabels()
      // Finished the creation of predefined labels
      // ==> We keep the number of predefined labels
{
   predefinedlabelnum=labelnum;
}

int CompLabelDict::CalcHashIdx(char *label,int len)
   // Computes the hash value for a given label name
{
   int val=0;

   while(len>0)
   {
      val+=*label;
      val=(val<<(*label&7))+(val>>(32-(*label&7)));
      label++;
      len--;
   }
   return ((val>>HASHTABLE_SHIFT)+(val>>HASHTABLE_SHIFT2)+(val>>HASHTABLE_SHIFT3)+val)&HASHTABLE_MASK;
}

TLabelID CompLabelDict::FindLabelOrAttrib(char *label,unsigned len,unsigned char isattrib)
   // Finds a given label (element tag or attribute)
{
   CompressLabelDictItem **hashref=hashtable+CalcHashIdx(label,len);

#ifdef PROFILE
   lookupcount++;
#endif

   // We look through the linked list of hashtable entries
   while(*hashref!=NULL)
   {
#ifdef PROFILE
      hashitercount++;
#endif
      if(((*hashref)->len==len)&&
         ((*hashref)->IsAttrib()==isattrib)&&
         (mymemcmp((*hashref)->GetLabelPtr(),label,len)==0))

         return (*hashref)->labelid;

      hashref=&((*hashref)->nextsamehash);
   }
   return LABEL_UNDEFINED;
}

TLabelID CompLabelDict::CreateLabelOrAttrib(char *label,unsigned len,unsigned char isattrib)
   // Creates a new label in the hash table
{
   // Let's get some memory first
   session->mainmem->WordAlign();
   CompressLabelDictItem *item=(CompressLabelDictItem *)session->mainmem->GetByteBlock(sizeof(CompressLabelDictItem)+len);
   session->mainmem->WordAlign();

   // We copy the label name
   item->len=(unsigned short)len;
   mymemcpy(item->GetLabelPtr(),label,len);

   // We insert it into the hashtable
   CompressLabelDictItem **hashref=hashtable+CalcHashIdx(label,len);
   item->nextsamehash=*hashref;
   *hashref=item;

   // Let's add the label to the list of labels
   item->next=NULL;

   *labelref=item;
   labelref=&(item->next);

   item->labelid=labelnum;
   labelnum++;

   if(isattrib)
      item->labelid|=ATTRIBLABEL_STARTIDX;

   return item->labelid;
}

TLabelID CompLabelDict::GetLabelOrAttrib(char *label,unsigned len,unsigned char isattrib)
   // Finds or creates a label/attribute
{
   TLabelID labelid=FindLabelOrAttrib(label,len,isattrib);

   if(labelid==LABEL_UNDEFINED)
      return CreateLabelOrAttrib(label,len,isattrib);
   else
      return labelid;
}

void CompLabelDict::Store(Compressor *compressor)
   // Stores the current content of the label dictionary in the output
   // compressor. Only the labels since the last storing are copied.
{
   MemStreamer mem(session);

   // Let's store the number of labels that were inserted
   // since the previous storing
   mem.StoreUInt32(labelnum-savedlabelnum);

   // We go through all new labels and store them.
   while(*savedlabelref!=NULL)
   {
      mem.StoreSInt32(((*savedlabelref)->labelid&ATTRIBLABEL_STARTIDX)?1:0,(*savedlabelref)->GetLabelLen());
      mem.StoreData((*savedlabelref)->GetLabelPtr(),(*savedlabelref)->GetLabelLen());
      savedlabelref=&((*savedlabelref)->next);
   }

   compressor->CompressMemStream(&mem);

   savedlabelnum=labelnum;
}

// ************** These are functions for the uncompressor ****************************

DecompLabelDict::DecompLabelDict(Session *s): LabelDict(s)
{
}

void DecompLabelDict::InitMore()
{
      // No labels until now
      labeldictlist=NULL;
      lastlabeldict=NULL;
}

void DecompLabelDict::Reset()
{
      // No labels until now
      labelnum=0;
      labeldictlist=NULL;
      lastlabeldict=NULL;
}

void DecompLabelDict::Load(SmallBlockUncompressor *uncompress)
      // Loads the next block of labels and appends the block to the
      // already existing blocks in the dictionary
   {
      UncompressLabelDictItem *dictitemptr;
      char                    isattrib;
      unsigned                dictlabelnum;

      // Let's get the number of labels first
      unsigned mylabelnum=uncompress->LoadUInt32();
      if(mylabelnum>MAXLABEL_NUM)
         ExitCorruptFile();

      // No new labels?
      if(mylabelnum==0)
         return;

      if(lastlabeldict!=NULL)
         // We already have some labels in the dictionary?
      {
         if(lastlabeldict->labelnum<LABELDICT_MINLABELNUM)
            // Is there some space for more labels in the current block?
            // We copy as many labels as we can
         {
            dictlabelnum=LABELDICT_MINLABELNUM-lastlabeldict->labelnum;
            if(dictlabelnum>mylabelnum)
               dictlabelnum=mylabelnum;

            mylabelnum-=dictlabelnum;

            dictitemptr=lastlabeldict->GetArrayPtr()+lastlabeldict->labelnum;

            lastlabeldict->labelnum+=dictlabelnum;
            labelnum+=dictlabelnum;

            // We copy the actual labels now
            // Each label is represented by the length and the attribute-flag
            // Then, the actual name follows
            while(dictlabelnum--)
            {
               dictitemptr->len=(unsigned short)uncompress->LoadSInt32(&isattrib);
               dictitemptr->isattrib=isattrib;

               dictitemptr->strptr=session->mainmem->GetByteBlock(dictitemptr->len);

               mymemcpy(dictitemptr->strptr,
                        uncompress->LoadData(dictitemptr->len),
                        dictitemptr->len);

               dictitemptr++;
            }
            if(mylabelnum==0)
               return;
         }
         // We create a new label block for the new labels
         lastlabeldict->next=(UncompressLabelDict *)session->mainmem->GetByteBlock(
                                 sizeof(UncompressLabelDict)+
                                 ((mylabelnum>LABELDICT_MINLABELNUM)? mylabelnum : LABELDICT_MINLABELNUM)*sizeof(UncompressLabelDictItem));
         lastlabeldict=lastlabeldict->next;
      }
      else
         labeldictlist=lastlabeldict=(UncompressLabelDict *)session->mainmem->GetByteBlock(
                                       sizeof(UncompressLabelDict)+
                                       ((mylabelnum>LABELDICT_MINLABELNUM)? mylabelnum : LABELDICT_MINLABELNUM)*sizeof(UncompressLabelDictItem));

      lastlabeldict->next=NULL;

//******

      lastlabeldict->labelnum=mylabelnum;
      labelnum+=mylabelnum;

      dictlabelnum=mylabelnum;

      dictitemptr=lastlabeldict->GetArrayPtr();

      // We copy the actual labels now
      // Each label is represented by the length and the attribute-flag
      // Then, the actual name follows
      while(dictlabelnum--)
      {
         dictitemptr->len=(unsigned short)uncompress->LoadSInt32(&isattrib);
         dictitemptr->isattrib=isattrib;

         dictitemptr->strptr=session->mainmem->GetByteBlock(dictitemptr->len);
         session->mainmem->WordAlign();

         mymemcpy(dictitemptr->strptr,
                  uncompress->LoadData(dictitemptr->len),
                  dictitemptr->len);

         dictitemptr++;
      }
   }

   unsigned long DecompLabelDict::LookupCompressLabel(TLabelID labelid,char **ptr,unsigned char *isattrib)
      // Find the name of the label with a given ID
   {
      UncompressLabelDict *labeldict=labeldictlist;

      // We look through all the blocks
      while(labelid >= labeldict->labelnum)
      {
         labelid-=labeldict->labelnum;
         labeldict=labeldict->next;
      }
      UncompressLabelDictItem *item=labeldict->GetArrayPtr()+labelid;

      *isattrib=item->isattrib;
      *ptr=item->strptr;
      return item->len;
   }

	unsigned long CompLabelDict::LookupCompressLabel(TLabelID labelid,char **ptr,unsigned char *isattrib)
      // Finds the name of a label with a given ID in the compressor
      // In the compressor, we need to traverse the entire list
      // of labels ==> Relatively inefficient - but we only
      // need that for printing
   {
      CompressLabelDictItem *item=labels;

      labelid=(labelid&(ATTRIBLABEL_STARTIDX-1));

      while(labelid--)
         item=item->next;

      *ptr=item->GetLabelPtr();
      return item->GetLabelLen();
   }

	UncompressLabelDictItem* UncompressLabelDict::GetArrayPtr()
      // Returns the pointer to the UncompressLabelDictItem
   {
      return (UncompressLabelDictItem *)(this+1);
   }
