/*
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 memory streamer class
// The memory streamer represents a sequence of memory blocks
// that grows over time. The blocks at the beginning are small, while
// later, larger blocks are allocated.
// The memory streamer is the core of the container implementation

#include "stdafx.h"

/* globals */
unsigned char blocksizeidxs[BLOCKSIZE_IDXNUM]={0,0,0,1,1,1,1,1,2,2,2,2,2,2,2,3};

// For the MemStreamer, the block size increase slowly in 16 steps.
// 'blocksizeidxs' contains the block size indices for each step

//class Output;

/* class MemStreamer implementation */
void MemStreamer::ReleaseBlock(MemStreamBlock *block)
   // Releases the block from the mem streamer
   // Typically, this is the last block in the chain - but it could
   // also be in the middle
{
/*
   if(block->myprev!=NULL)
      block->myprev->mynext=block->mynext;
   else
      blocklist=block->mynext;
   if(block->mynext!=NULL)
      block->mynext->myprev=block->myprev;
*/
//      memset(block->data,0xcd,block->cursize);
   session->FreeBlock((char *)block,blocksizeidxs[block->blocksizeidxidx]);
}

MemStreamBlock *MemStreamer::AllocateNewBlock()
   // Allocates a new block
   // The size is determined by the next index in blocksizeidxs
{
   MemStreamBlock *newblock;

   // Let's get the new block
   newblock=(MemStreamBlock *)session->AllocateBlock(blocksizeidxs[curblocksizeidxidx]);

   // The usable data size is the block size - the size of the header
   newblock->blocksize=session->GetBlockSize(blocksizeidxs[curblocksizeidxidx])-(sizeof(MemStreamBlock)-1);
   newblock->blocksizeidxidx=curblocksizeidxidx;

   // Do we still have more steps to go in the block size increase?
   // => Go to next step
   if(curblocksizeidxidx<BLOCKSIZE_IDXNUM-1)
      curblocksizeidxidx++;

   newblock->next=NULL;
   newblock->cursize=0;
/*
   // Let's insert the block at the end of the block-chain
   newblock->mynext=blocklist;
   if(blocklist!=NULL)
      blocklist->myprev=newblock;
   newblock->myprev=NULL;
   blocklist=newblock;
*/
   return newblock;
}

void MemStreamer::Initialize(Session *s, unsigned long firstblocksizeidx)
   // Initializes the memstreamer and sets the first block index step
{
	session = s;
   if(firstblocksizeidx>=BLOCKSIZE_IDXNUM)
      firstblocksizeidx=BLOCKSIZE_IDXNUM-1;

   curblocksizeidxidx=firstblocksizeidx;
   overallsize=0;

   curmarker.block=NULL;
   curmarker.pos=NULL;

   firstblock=curblock=NULL;
}

MemStreamer::MemStreamer(Session *s,unsigned long firstblocksizeidx)
{
   Initialize(s, firstblocksizeidx);
}

MemStreamer::~MemStreamer()
{
   ReleaseMemory(0);
}

int MemStreamer::GetSize() 
{ 
	return overallsize; 
}

MemStreamBlock *MemStreamer::GetFirstBlock() 
{ 
	return firstblock; 
}

void MemStreamer::WordAlign()
   // Allocated 1 to 3 bytes to align the current memory pointer
   // to an address divisible by 4.
{
   if(curblock==NULL)
      return;

   int addsize=3-((curblock->cursize+3)&3);
   if(addsize>0)
   {
      curblock->cursize+=addsize;
      overallsize+=addsize;
   }
}

char *MemStreamer::GetByteBlock(unsigned len)
   // The main function for allocated more memory of size 'len'.
   // The function checks the current block and if there is not enough space,
   // the function 'AllocateNewBlock' is called.
{
   if(len+sizeof(MemStreamBlock)-1>LARGEBLOCK_SIZE) // Is the requested size larger than the biggest possible block?
   {
      char str[STRLEN+1];
      _snprintf(str, STRLEN, "Could not allocate %lu bytes (largest possible block size=%lu bytes) !",
         sizeof(MemStreamBlock)-1+len,
         LARGEBLOCK_SIZE);
		str[STRLEN] = '\0';
      throw new XMillException(XMILL_ERR_BLOCK_TOO_LARGE, str);
   }

   if(curblock==NULL)
      // We don't have a block yet ?
      firstblock=curblock=AllocateNewBlock();

   if(curblock->blocksize-curblock->cursize>=len)
      // Enough space in current block?
   {
      char *ptr=curblock->data+curblock->cursize;
      curblock->cursize+=len;
      overallsize+=len;
      return ClearBlock(ptr,len);
	} else { // We add a new block at the end
      do
      {
         curblock->next=AllocateNewBlock();
         curblock=curblock->next;
      }
      while(curblock->blocksize<len);  // If we don't have enough space,
                                       // we simply create a bigger one!

      curblock->cursize=len;
      overallsize+=len;

      return ClearBlock(curblock->data,len);
   }
}

char *MemStreamer::ClearBlock(char *data, unsigned len)
{
	for (unsigned int i=0; i<len; i++) {
		data[i] = 0;
	}
	return data;
}

void MemStreamer::ReleaseByteBlock(unsigned len)
   // Removes 'len' bytes from the current block
{
   // If the current block is smaller than the data to be removed,
   // then there is something really wrong!
   if(curblock->cursize<len)
   {
      throw new XMillException(XMILL_ERR_FATAL, "Fatal error in ReleaseBlock !\n");
   }

   curblock->cursize-=len;
   overallsize-=len;

#ifdef RELEASEMEM_SAFE
   memset(curblock->data+curblock->cursize,0xcd,len);
#endif
}

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

void MemStreamer::StoreData(char *data,unsigned len)
   // Stores the a sequence of bytes in the memory streamer
   // The bytes can be distributed over many blocks
{
   if(curblock==NULL)
      // We don't have a block yet ?
      firstblock=curblock=AllocateNewBlock();

   while(len>curblock->blocksize-curblock->cursize)
      // There is not enough space in the current block ?
      // ==> We fill the current block as much as possible
   {
      mymemcpy(curblock->data+curblock->cursize,data,
               curblock->blocksize-curblock->cursize);

      data        +=curblock->blocksize-curblock->cursize;
      overallsize +=curblock->blocksize-curblock->cursize;
      len         -=curblock->blocksize-curblock->cursize;
      curblock->cursize=curblock->blocksize;

      curblock->next=AllocateNewBlock();

      curblock=curblock->next;
   }
   mymemcpy(curblock->data+curblock->cursize,data,len);
   curblock->cursize+=len;
   overallsize+=len;
}

//******************************************************************************
// The following auxiliary methods allow the easy storage of simple data values

void MemStreamer::StoreChar(unsigned char c)
   // Stores one single character
{
   if(curblock==NULL)
      // We don't have a block yet ?
      firstblock=curblock=AllocateNewBlock();
   else
   {
      if(curblock->blocksize-curblock->cursize==0)
         // The block is completely full? ==> Get a new one
      {
         curblock->next=AllocateNewBlock();
         curblock=curblock->next;
      }
   }
   curblock->data[curblock->cursize]=c;
   curblock->cursize++;
   overallsize++;
}

void MemStreamer::StoreCompressedUInt(unsigned long val,unsigned char offs)
   // Stores a compressed unsigned integer
   // The values below the offset threshold are reserved for other use.
   // Hence, the offset is basically added to 'val'.
   // The offset *must* be smaller than 128
{
   if (val<128-(unsigned)offs)
		/* 7 bit number: bit header = 0 */
      StoreChar((unsigned char)val+offs);
   else if (val < 1<<14) {
		/* 14 bits number: bit header = 10 */
      StoreChar((unsigned char)(val>>8)+128);
      StoreChar((unsigned char)val);
	} else if (val < 1<<29) {
		/* 29 bits number: bit header = 110 */
      StoreChar((unsigned char)(val>>24)+192);
      StoreChar((unsigned char)(val>>16));
      StoreChar((unsigned char)(val>>8));
      StoreChar((unsigned char)(val));
	} else {
		/* 32 bits number: bit header = 111 (other 5 bits unused) */
      StoreChar(224);
      StoreChar((unsigned char)(val>>24));
      StoreChar((unsigned char)(val>>16));
      StoreChar((unsigned char)(val>>8));
      StoreChar((unsigned char)(val));
   }
}

void MemStreamer::StoreCompressedSInt(char isneg,unsigned long val)
   // Stores a signed integer in compressed format
   // The sign is in 'isneg' and 'val' is the basis
{
	if(val < 64) {
		/* 6 bits: bit header = 0x */
      StoreChar((unsigned char)val+(isneg ? 64 : 0));
	} else if (val < 1<<13) {
		/* 13 bits: bit header = 10x */
      StoreChar((unsigned char)(val>>8)+(isneg ? (128+32) : 128));
      StoreChar((unsigned char)val);
   } else if (val < 1<<28) {
		/* 28 bits: bit header = 11x0 */
		StoreChar((unsigned char)(val>>24)+(isneg ? (192+32) : 192));
		StoreChar((unsigned char)(val>>16));
		StoreChar((unsigned char)(val>>8));
		StoreChar((unsigned char)val);
   } else {
		/* 31 bits: bit header = 11x1 (other 4 bits unused) */
		StoreChar((isneg ? (208+32) : 208));
		StoreChar((unsigned char)(val>>24));
		StoreChar((unsigned char)(val>>16));
		StoreChar((unsigned char)(val>>8));
		StoreChar((unsigned char)val);
   }
}

void MemStreamer::StoreCompressedSInt(long val)
   // Stores a signed integer in compressed format
{
   if(val&0x80000000L)
      StoreCompressedSInt(1,-(long)val);
   else
      StoreCompressedSInt(0,val);
}

void MemStreamer::StoreUInt32(unsigned long val)
   // Stores a unsigned integer in compressed format
{
   StoreCompressedUInt(val);
}

void MemStreamer::StoreSInt32(char isneg,unsigned long val)
   // Stores a signed integer in compressed format
{
   StoreCompressedSInt(isneg,val);
}

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

void MemStreamer::StartNewMemBlock()
   // Creates a new marker on the memory streamer
{
   MemStreamMarker *newmarker=(MemStreamMarker *)GetByteBlock(sizeof(MemStreamMarker));

   *newmarker=curmarker;   // We copy previous marker

   // We save the new marker position
   curmarker.block=curblock;
   curmarker.pos=newmarker;
}

void MemStreamer::RemoveLastMemBlock()
   // Removes a the data up to the last memory marker
{
   MemStreamBlock *block,*nextblock;
   int            newsize;

   // We remove all blocks after the block of the last marker

   block=curmarker.block->next;

   while(block!=NULL)
   {
      overallsize-=block->cursize;
      nextblock=block->next;
      ReleaseBlock(block);
      block=nextblock;
   }
   curmarker.block->next=NULL;

   // We save block pointer -> This block will be truncated to size 'newsize'
   block=curblock=curmarker.block;

   // The remaining size
   newsize=(char *)curmarker.pos-block->data;

   // We set the marker
   curmarker=*(curmarker.pos);

#ifdef RELEASEMEM_SAFE
   // We overwrite the empty space ==> Just for protection
   // We can take this out later
   memset(block->data+newsize,0xcd,block->cursize-newsize);
#endif

   overallsize-=(block->cursize-newsize);
   block->cursize=newsize;

   if(block->blocksizeidxidx<BLOCKSIZE_IDXNUM-1)
      // Not the largest block size?
      curblocksizeidxidx=block->blocksizeidxidx+1;
   else
      curblocksizeidxidx=block->blocksizeidxidx;
}

void MemStreamer::ReleaseMemory(unsigned long firstblocksizeidx)
   // Releases the entire memory of the memstreamer
{
   MemStreamBlock    *block=firstblock,
                     *nextblock;
   while(block!=NULL)
   {
      nextblock=block->next;
      ReleaseBlock(block);
      block=nextblock;
   }
   Initialize(session, firstblocksizeidx);
}

void *MemStreamer::operator new(size_t size, MemStreamer *mem)
{  
	return mem->GetByteBlock(size);  
}
void MemStreamer::operator delete(void *ptr) {}

/* old code down here */
//*********************************************************************
//*********************************************************************
// For the decompressor, we implement an additional memory management

/* #ifdef XDEMILL

extern MemStreamer blockmem;

#define MEMBLOCK_THRESHOLD 8000

extern unsigned char *session->memoryalloc_buf;
extern unsigned char *session->memoryalloc_curptr;
extern unsigned long session->memoryalloc_bufsize;

inline void SetMemoryAllocationSize(unsigned long allocsize)
{
   if(session->memoryalloc_bufsize<allocsize)
   {
      if(session->memoryalloc_buf!=NULL)
         free(session->memoryalloc_buf);

      session->memoryalloc_buf=(unsigned char *)malloc(allocsize);
      if(session->memoryalloc_buf==NULL)
         ExitNoMem();

      session->memoryalloc_curptr=session->memoryalloc_buf;

      session->memoryalloc_bufsize=allocsize;
   }
   else
      session->memoryalloc_curptr=session->memoryalloc_buf;

}

inline void WordAlignMemBlock()
{
   session->memoryalloc_curptr=
      (unsigned char *)
      (((unsigned long)session->memoryalloc_curptr+3)&0xFFFFFFFCL);
}

inline unsigned char *AllocateMemBlock(unsigned long size)
{
   session->memoryalloc_curptr+=size;
   if(session->memoryalloc_curptr>session->memoryalloc_buf+session->memoryalloc_bufsize)
   {
      Error("Fatal Error!");
      Exit();
   }
   return session->memoryalloc_curptr-size;
}

inline void FreeMemBlock(void *ptr,unsigned long size)
{
}
*/
