/*
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 input file class
// Based on class 'CFile', the new class 'Input' implements the
// buffer management

#include "stdafx.h"

// A macro for refilling the buffer to *at least* 'mylen' characters
// If there are not enough characters in the file, then the program exits
#if 1
inline int Input::FillBufLen(int mylen)
	{
		if(endptr-curptr<mylen) {
			FillBuf();
			if(endptr-curptr<mylen) {
				return FALSE;
			}
		}
		return TRUE;
	}
#else
	#define FillBufLen(mylen)  \
		if(endptr-curptr<(mylen)) {\
			FillBuf(); \
			if(endptr-curptr<(mylen)) {\
				throw new XMillException(XMILL_ERR_PARSE, "Unexpected end of file!");\
			}}
#endif

/* class Input implementation */
Input::Input(Session *s)
{
	session = s;
	thefile = NULL;
	bytesreadptr = NULL;
	reInit();
}

Input::~Input()
{
	trydel(thefile);
}

void Input::reInit() 
{
   curptr=endptr=NULL;
   curlineno=1;
	/*if (bytesreadptr) {
		*bytesreadptr = 0;
	}*/
	trydel (thefile);
	thefile = new CFile();
}

void Input::setBytesReadPtr(int *b)
{
	if ((bytesreadptr = b)) {
		*bytesreadptr = 0;
	}
}

int Input::increaseBytesRead(int len)
{
	if (bytesreadptr) {
		*bytesreadptr += len;
	}
	return len;
}

/* 4 methods added because the CFile relation has changed from
is-a to has-a */
unsigned Input::GetFilePos()  
{ 
	return thefile->GetFilePos();
}
unsigned Input::ReadBlock(char *dest,unsigned bytecount)
{
	return thefile->ReadBlock(dest, bytecount);
}
void Input::CloseFile() 
{
	thefile->CloseFile();
}
void Input::SetCFile(CFile *cfile) 
{ 
	trydel(thefile);
	thefile = cfile;
}

void Input::FillBuf()  // The function fills the buffer as much as possible
{
   int bytesread;

   if(endptr-curptr>0)
      // Is there some unread data ?
   {
      memmove(databuf,curptr,endptr-curptr);
      endptr=databuf+(endptr-curptr);
      curptr=databuf;
   }
   else
      curptr=endptr=databuf;

   // We try to fill the rest of the buffer
   bytesread=ReadBlock(endptr,databuf+FILEBUF_SIZE-endptr);

   endptr+=bytesread;
}

char Input::OpenFile(char *filename)
   // Opens the file and fills the buffer
{
   if(thefile->OpenFile(filename)==0)
      return 0;

   curptr=endptr=databuf;

   FillBuf();

   curlineno=1;

   return 1;
}

char Input::ReadData(char *dest,int len)
   // Reads 'len' characters into the buffer 'dest'
   // If the data is already in memory, we simply copy
   // Otherwise, we iteratively read more from the file
   // The functions returns 0 if 'len' bytes are read
   // It returns 1, if the end of the file has been reached
{
   int savelen=len;

	/* first handle all data that requires reading extra blocks */
   while(endptr-curptr<len)
   {
      if(IsEndOfFile())
			/* couldn't fulfill request for 'len' Bytes */
         return TRUE;

		/* read data from this block */
		len -= increaseBytesRead(endptr-curptr);
      while(curptr<endptr)
      {
         *dest=*curptr;
         if(*curptr=='\n')
            curlineno++;
         dest++;
         curptr++;
		}

		/* get next block */
      FillBuf();
   }

	/* no extra blocks needed; read 'len' Bytes from the current */
	increaseBytesRead(len);
	while(len>0)
   {
      *dest=*curptr;
      if(*curptr=='\n')
         curlineno++;
      dest++;
      curptr++;
      len--;
   }
   return FALSE;
}

char Input::GetChar(char *ptr)
   // Reads one single character and stores it in *ptr
{
	if(!FillBufLen(1)) {
		return FALSE;
	}
	increaseBytesRead();
   *ptr=*curptr;
   if(*curptr=='\n')
      curlineno++;
   curptr++;
	return TRUE;
}

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

char Input::ReadFullUInt32(unsigned long *data)
   // Reads a full integer (32 bit)
{
	if (!FillBufLen(sizeof(unsigned long))) {
		return FALSE;
	}
	increaseBytesRead(sizeof(unsigned long));

   *data=*(unsigned long *)curptr;
   curptr+=sizeof(unsigned long);
	return TRUE;
}

char Input::ReadUInt32(unsigned long *data)
   // Reads a compressed integer
   // If the MSB (most significant bit) of the first byte is 0, then
   // we have a value <128
   // If the MSB is zero, then we look at the second MSB - if it is 0, then
   // we have a value <16384 
	// If the third MSB is zero, then we have a value <2^29
	// Else, we have 32 bits number
{
	if (!FillBufLen(1)) {
		return FALSE;
	}
	increaseBytesRead();

	if (*(unsigned char *)curptr<128) {
		/* 7 bits */
      *data=(unsigned)(unsigned char)*(curptr++);
	} else if (*(unsigned char *)curptr<192) {
		/* 14 bits */
      if (!FillBufLen(1)) {
			return FALSE;
		}
		increaseBytesRead();
      *data=(((unsigned)(unsigned char)*(curptr++)-128)<<8)+(unsigned)(unsigned char)*(curptr++);
   } else if (*(unsigned char *)curptr<224) {
		/* 29 bits */
	   if (!FillBufLen(3)) {
			return FALSE;
		}
		increaseBytesRead(3);

      *data=   (((unsigned)(unsigned char)*(curptr++)-192)<<24)+
               (((unsigned)(unsigned char)*(curptr++))<<16)+
               (((unsigned)(unsigned char)*(curptr++))<<8)+
               (unsigned)(unsigned char)*(curptr++);
   } else {
		/* 32 bits */
      if(!FillBufLen(4)) {
			return FALSE;
		}
		increaseBytesRead(4);
		curptr++;

      *data=   (((unsigned)(unsigned char)*(curptr++))<<24)+
               (((unsigned)(unsigned char)*(curptr++))<<16)+
               (((unsigned)(unsigned char)*(curptr++))<<8)+
               (unsigned)(unsigned char)*(curptr++);
   }
	return TRUE;
}

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

char Input::PeekData(char *ptr,int len)
   // The functions looks ahead 'len' bytes and stores them in 'ptr'
{
	if (!FillBufLen(len)) {
		return FALSE;
	}
   mymemcpy(ptr,curptr,len);
	return TRUE;
}

char Input::PeekChar(char *ptr)
   // Peeks one character ahead and stores it in *ptr
   // Returns -1 if there was an error
   // Returns -2 if there is no character left
   // Returns 0 if everything is okay
{
	if (!FillBufLen(1)) {
		return FALSE;
	}
   *ptr=*curptr;
	return TRUE;
}

void Input::SkipData(int len)
   // Skips 'len' characters
   // If the data is already in memory, we simply skip
   // Otherwise, we iteratively read more from the file
   // The functions returns 0 if 'len' bytes are skipped
   // Otherwise, it returns -1, if there was some I/O error
   // If we reached EOF, we return -2
{
   while(endptr-curptr<len)
   {
      len -= increaseBytesRead(endptr-curptr);
      curptr=endptr=databuf;

      FillBuf();
   }
	increaseBytesRead (len);
   curptr += len;
}

void Input::FastSkipData(int len)
   // Does a fast skip - the data is already expected to be in the buffer
{
   curptr += increaseBytesRead(len);
}

char Input::SkipChar()
   // Skips one single character
{
	if (!FillBufLen(1)) {
		return FALSE;
	}
   if(*curptr=='\n')
      curlineno++;
   curptr++;
	return TRUE;
}

char Input::IsEndOfFile(unsigned len)
   // Checks whether we reached the end of the file
{
   return (curptr+len==endptr)&&(thefile->IsEof());
}

int Input::GetCurBlockPtr(char **ptr)
   // Returns the length of rest of data in the buffer and
   // stores the data pointer in *ptr
{
   *ptr=curptr;
   return endptr-curptr;
}

void Input::RefillAndGetCurBlockPtr(char **ptr,int *len)
   // Refills and
   // returns the length of rest of data in the buffer and
   // stores the data pointer in *ptr
{
   FillBuf();
   *ptr=curptr;
   *len=endptr-curptr;
}

void Input::UndoReadChar(int len)
   // Unreads the last characters that were read
{
	increaseBytesRead(-len);
   curptr -= len;
}

/* class MemFile implementation */
MemFile::MemFile(char *b,int buflen): CFile()
{
	OpenFile(b,buflen);
}

char MemFile::OpenFile(char *b,int blen)
{
	buffer = b;
	bytesleft = buflen = (blen == 0 ? strlen(b) : blen);
	filepos = 0;

	return 1;
}

unsigned MemFile::ReadBlock(char *dest,unsigned bytecount)
   // Reads a data block into the memory at 'dest'. The maximum size is 'bytecount'
   // The function returns the number of bytes read or -1, if something fails
   // If the result is smaller than bytecount, the end of the file has been reached
   // and flag eof is set to 1.
{
   if(iseof)
      return 0;

   // let's try to reach 'bytecount' bytes
   unsigned bytesread=(unsigned)min(bytecount,bytesleft);
	memcpy(dest, &buffer[filepos], bytesread);
	bytesleft -= bytesread;
   filepos+=bytesread;

	if(bytesleft == 0) {
      iseof=1;
   }
   return bytesread;
}

void MemFile::CloseFile() {};

/* class MemInput implementation */
MemInput::MemInput(Session *s): Input(s)
{
	memfile = NULL;
}

MemInput::MemInput(Session *s, char *membuf, int buflen): Input(s)
{
	memfile = new MemFile(membuf, buflen);
   SetCFile(memfile);
	memfile = NULL;
}

MemInput::~MemInput()
{
	trydel (memfile);
}

void MemInput::setData(char *membuf, int buflen)
{
	reInit();
	trydel (memfile);
	memfile = new MemFile(membuf, buflen);
   SetCFile(memfile);
	memfile = NULL;
}
