/*===========================================================================
  Copyright (C) 1987-2009 European Southern Observatory (ESO)
 
  This program is free software; you can redistribute it and/or 
  modify it under the terms of the GNU General Public License as 
  published by the Free Software Foundation; either version 2 of 
  the License, or (at your option) any later version.
 
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
 
  You should have received a copy of the GNU General Public 
  License along with this program; if not, write to the Free 
  Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, 
  MA 02139, USA.
 
  Correspondence concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.TYPE		Module
.IDENTIFICATION	buffer.c
.AUTHOR    	Francois Ochsenbein [ESO-IPG]
.LANGUAGE  	C
.KEYWORDS	Buffer / Stack Management
.ENVIRONMENT 	
.COMMENTS
	These routines allow the management of buffers defined
	as the following BUFFER structure:
\begin{TeX}
\begin{itemize}
\item	{\tt char *buf} is the address of the buffer in memory
\item	{\tt int allocated}	is the number of bytes allocated to buf
\item	{\tt int used}	is the present number of bytes used in this
		buffer
\item	{\tt int increment}	is the number of bytes for automatic expansions
\item	{\tt offset}	is an integer, \eg
		to perform some searches. It is used for stacking and unstacking.
\end{itemize}
\end{TeX}
.VERSION 1.0	03-Feb-1987: Creation
.VERSION 1.1	20-May-1987: Removed bug in mm_set
.VERSION 2.0	20-Apr-1988: Changed function names, added stacking facilities.
.VERSION 2.1	01-Jun-1988: Added mm_zloc (retrieve a zero item),
			mm_zindex (retrieve non-zero item)
			mm_zfree (free the index)
.VERSION 2.2	07-Nov-1988: Removed bug in mm_bunst
.VERSION 2.3	07-Jun-1991: Be sure an EOS is appended in mm_bapp

 090831		last modif
----------------------------------------------------------------------------*/

#define	DEBUG		0	/* For debugging purposes	*/

#if DEBUG
#define	PM_LEVEL	LEVEL_STR
#endif

#define  PASCAL_DEF	0	/* Don't include pascalisation ... */

#include <midas_def.h>	/* Standard Definitions	*/
#include <stesodef.h>	/* Standard Definitions	*/
#include <buffer.h>


extern int mm_free(), eh_put1(), eh_ed_i();



#define FINISH		goto FIN

/*==========================================================================*/
BUFFER *mm_bopen(size, incr)    
/*+++
.PURPOSE Allocation of a buffer of a given size
.RETURNS Address of allocated buffer, or NULL address if failed,
.REMARKS If incr is zero, the buffer cannot be automatically expanded.
-------*/
	int	size;	/* IN: The required size (bytes)	*/
	int	incr;	/* IN: The increment size for expansion (bytes)	*/
{
	register BUFFER *b;

  ENTER("*mm_bopen");

  if (size < 0)		size = 0;
  b = MEM_GET(BUFFER,1);
  b->buf 	= NULL_PTR(char);
  b->allocated	= size;
  b->used	= 0;
  b->increment	= (incr > 0 ? incr : 0);
  b->offset	= 0;

  if (b->allocated)
  	b->buf = MEM_GET(char, b->allocated);

  EXIT_PTR(BUFFER, b);
}  

/*==========================================================================*/
int	mm_bfree(b)    
/*+++
.PURPOSE Free a BUFFER structure --- but ONLY the buffer!
.RETURNS OK
.REMARKS 
---*/
	BUFFER	*b;	/* IN: The buffer to free	*/
{

  ENTER("mm_bfree");

  if (b)
  {	MEM_FREE(b->buf);
	b->buf 		= NULL_PTR(char);
	b->allocated 	= 0;
	b->used		= 0;
	b->offset	= 0;
  }

  EXIT(OK);
}  

/*==========================================================================*/
int mm_bexp(b, size)    
/*+++
.PURPOSE Expansion of a buffer
.RETURNS OK (1)/ NOK(0)
.REMARKS Size of zero implies "use increment"
---*/
	BUFFER *b;	/* IN: The Buffer to Expand		*/
	int	size;	/* IN: The required new size (bytes)	*/
{
	register char *p;
	int new_size, status;
	
  ENTER("mm_bexp");

  new_size = (size > 0 ? size : b->allocated + b->increment);

  p = MEM_EXP(char, b->buf, new_size);
  if (p)  	b->allocated = new_size, b->buf = p, status = OK;
  else		status = NOK;

  EXIT(status);
}

/*==========================================================================*/
char *mm_bst(b, record, len)    
/*+++
.PURPOSE Stack a record into buffer --- The buffer is
	expanded if necessary
.RETURNS Address of stacked record / NULL if error
.REMARKS Save offset of previous stacked values.
---*/
	BUFFER	*b;		/* IN: The buffer to free	*/
	char	*record;	/* IN: record to append		*/
	int	len;		/* IN: Length of record		*/
{
	char	*a;
	
  ENTER("*mm_bst");
  
  a = mm_ball(b, len + sizeof(int));
  if (a)
  {  	a += oscopy(a, (char *)(&(b->offset)), sizeof(int));
	b->offset = a - b->buf;
  	oscopy(a, record, len);
  }

  EXITp(a);
}

/*==========================================================================*/
char *mm_bunst(b)
/*+++
.PURPOSE Unstack a record from buffer.
.RETURNS Address of record / NULL
.REMARKS 
---*/
	BUFFER	*b;		/* IN: The buffer to free	*/
{
	char	*p;
	
  ENTER("*mm_bunst");
  
  if (b->used <= 0)	p = NULL_PTR(char);
  else
  {	b->used = b->offset - sizeof(int);
	oscopy((char *)(&(b->offset)), b->buf + b->used, sizeof(int));
  	if (b->used <= 0)	p = NULL_PTR(char);
	else			p = b->buf + b->offset;
  }

  EXITp(p);
}

/*==========================================================================*/
char *mm_bapp(b, record, len)    
/*+++
.PURPOSE Append a record to a buffer --- The buffer is
	expanded if necessary
.RETURNS Address of stored record / NULL if error
.REMARKS 
---*/
	BUFFER	*b;		/* IN: The buffer to free	*/
	char	*record;	/* IN: record to append		*/
	int	len;		/* IN: Length of record		*/
{
	char	*p;
	
  ENTER("*mm_bapp");

	/* Get a pointer in buffer, and then copy string
	 * to this buffer	*/

  p = mm_ball(b, len+1);
  if (p) {
  	oscopy(p, record, len), p[len] = 0;
  	b->used -= 1;	
  }

#if DEBUG
	TRACE_ED_STR2("Buffer: ",b->buf, b->used);
#endif

  EXITp(p);
}  

/*==========================================================================*/
char *mm_ball(b, len)    
/*+++
.PURPOSE Set a pointer to next available record in buffer.
	Buffer is expanded if necessary
.RETURNS Address of available record / NULL if error
.REMARKS 
---*/
	BUFFER	*b;		/* IN: The buffer to free	*/
	int	len;		/* IN: Length of record		*/
{
	char	*p;
	int	ex;
	
  ENTER("*mm_ball");

#if DEBUG
  TRACE_ED_I("Asks for bytes: ",len);
#endif

  p = NULL_PTR(char);
  if_not(b) 		{ ERROR("Bad Buffer"); FINISH; }
  if (len < 0)		{ ERR_ED_I("Bad Length: ", len); FINISH; }

  p = (b->buf) + b->used;
  if (len  ==  0 )		FINISH;

#if DEBUG
  TRACE_ED_I("Already in buffer: ",b->used);
  TRACE_ED_STR2("Already in buffer: ",b->buf, b->used);
#endif

  ex = b->used + len - b->allocated;
  if ( ex > 0)			/* Doesn't fit. Expand is required */
  {	if(b->increment)	/* Increment is possible */
  	{	ex = (ex+b->increment-1)/b->increment;
  		ex = ex*b->increment + b->allocated;
  		p = (mm_bexp(b, ex) ? b->buf + b->used : NULL_PTR(char));
	}
	else	p = NULL_PTR(char);
  }
  else	;

  FIN:
  if(p)	b->used += len;

  EXITp(p);
}

/*==========================================================================*/
char *mm_zfree(b, index, item_len)    
/*+++
.PURPOSE Free the indexed item.
.RETURNS Address of freed item / NULL if bad item
.REMARKS No tracing.
---*/
	BUFFER	*b;		/* IN: The buffer to free	*/
	int	index;		/* IN: Index of item to free	*/
	int	item_len;	/* IN: Size of 1 item		*/
{
	char	*p;
	int	o;
	
  p = NULL_PTR(char);
  
	/* Find first the position */

  o = index * item_len;
  if ( (o < 0) || (o >= b->used) )	FINISH;
  p = b->buf + o;
  
  oscfill(p, item_len, '\0');

  FIN:
  return(p);
}

/*==========================================================================*/
char *mm_zindex(b, index, item_len)    
/*+++
.PURPOSE Finds the position of indexed item. Null item is checked.
.RETURNS Address of found item / NULL if bad item or is filled
	with zeroes.
.REMARKS No tracing.
---*/
	BUFFER	*b;		/* IN: The buffer to free	*/
	int	index;		/* IN: Index of item to free	*/
	int	item_len;	/* IN: Size of 1 item		*/
{
	char	*p;
	int	o;
	
  p = NULL_PTR(char);
  
	/* Find first the position */

  o = index * item_len;
  if ( (o < 0) || (o >= b->used) )	FINISH;
  p = b->buf + o;
  
  if (oscskip(p, item_len, '\0') == item_len)	/* Filled with zeroes... */
	p = NULL_PTR(char);

  FIN:
  return(p);
}

/*==========================================================================*/
char *mm_zloc(b, item_len)    
/*+++
.PURPOSE Finds the position for a new (free) item. The found
	position is filled with zeroes, and b->offset is initialized.
.RETURNS Address of found item position / NULL if impossible.
.REMARKS No tracing.
---*/
	BUFFER	*b;		/* IN: The buffer to free	*/
	int	item_len;	/* IN: Size of 1 item		*/
{
	char	*p;
	

	/* Look first if space remains. If yes, use it */

  if (b->allocated > b->used)
  {	b->offset = b->used;
  	FINISH;
  }

	/* Find an position with only zeroes	*/

  for (b->offset = 0; b->offset < b->used; b->offset += item_len)
  	if (oscskip(b->buf + b->offset, item_len, '\0') == item_len)
  		FINISH;

	/* Last possibility: Must expand the buffer... 
	 * Remember that b->offset is correctly set to b->used */
	
  mm_ball(b, item_len);

	/* Now, initialize the found item	*/

  FIN:
  if (b->offset < b->allocated)		/* It's OK... */
  {	p = b->buf + b->offset;
  	oscfill(p, item_len, '\0');
	if (b->used <= b->offset)	b->used = b->offset + item_len;
  }
  else	
  {	ERROR("Maximum reached.");
	p = NULL_PTR(char);
  }

  return(p);
}

