// -*- c++ -*-
//------------------------------------------------------------------------------
//                         Streambuf.h
//------------------------------------------------------------------------------
//  Copyright (c) 1999 by Vladislav Grinchenko
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Library General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//------------------------------------------------------------------------------
//  Created: 12/02/1999
//------------------------------------------------------------------------------
#ifndef STREAM_BUF_H
#define STREAM_BUF_H

#include <stdio.h>		// EOF
#include "assa/Assert.h"		// trace() & Assert family

namespace ASSA {

/** @file Streambuf.h 

Streambuf class is based on Standard C++ iostream streambuf class.
*/

/** 
 * io_ptrs structure.
 */
class io_ptrs
{
public:
	char* m_read_base;
	char* m_read_ptr;
	char* m_read_end;

	char* m_write_base;
	char* m_write_ptr;
	char* m_write_end;

	char* m_buf_base;
	char* m_buf_end;

	enum { USER_BUF = 1, UNBUFFERED = 2, EOF_SEEN = 4, ERR_SEEN = 8 };

	int   m_flags;
	char  m_shortbuf[1];

	io_ptrs () : m_read_base (0), m_read_ptr (0), m_read_end (0),
		     m_write_base (0), m_write_ptr (0), m_write_end (0),
		     m_buf_base (0), m_buf_end (0), m_flags (0)
		{ 
			m_shortbuf [0] = 0; 
		}

	void dump () const;
};

/** 
 * Streambuf class.
 *
 * Streambuf class is based on Standard C++ iostream streambuf class.
 *
 * Extending std::streambuf is always pain due to the obscurity and
 * complexity of its interface, and in general, lack of the source code
 * needed to efficiently understand its implementation. 
 *
 * I wrote my own Streambuf that implements a subset of 
 * std::streambuf functions - a bare minimum to get by 
 * for Socket buffering.
 *
 * The buffer of a  Streambuf  may be considered  to  have  three
 * parts:   the  get  area,  the put area, and the reserve area
 * (which is the same as the buffer area).  
 *
 * The get  area  contains the  characters immediately available for input.  
 *
 * The put area holds characters stored for output but not yet consumed
 * by  (flushed to) their ultimate destination.  The get
 * and put areas may be disjoint or may overlap (in my implementation
 * they are two disjoined buffers).   The  reserve
 * area  is  the  entire  buffer, overlapped by the get and put
 * areas (in my implementation, reserve area covers get area only).  
 * The get and put areas may expand into the  remainder
 * of  the  reserve  area.   In  the course of input and output
 * operations, the sizes of the get and put  areas  expand  and
 * shrink, always bounded by the total buffer size.
 */

class Streambuf : public io_ptrs
{
public:
	/// Maximum TCP frame size (with TCP options)
	static const int MAXTCPFRAMESZ;

	virtual ~Streambuf ();

	/** Set buffer.
	 *  @return setbuf(s_, n_)
	 */
	Streambuf* pubsetbuf (char* s_, int n_);

	/**
	 * @see sync
	 * @return sync()
	 */
	int        pubsync ();

	/** This function returns the number of characters  
	    immediately  available in the get area.  It is certain that i
	    characters may be fetched without  error,  and  without
	    accessing any external device.
	*/
	int        in_avail ();

	/** This function moves the get pointer forward  one  
	    position,   then   returns  the  character  after  the  get
	    pointer's new position.  If the get pointer is  at  the
	    end  of  the  sequence before or after the call to this
	    function (no character  is  available),  this  function
	    returns  EOF.  Example:  Suppose the input buffer looks
	    like this: abc|def
	    where `|' marks the position of the get pointer.   This
	    function will advance the get pointer and return `e'.
	*/
	int        snextc ();

	/** This  function  should  probably   have   been   called
	    ``sgetc''.   It moves the get pointer forward one posi-
	    tion and returns the character it moved past.   If  the
	    get  pointer  is  currently at the end of the sequence,
	    this function returns EOF.
	*/
	int        sbumpc ();

	/** This function  returns  the  character  after  the  get
	    pointer, or EOF if the get pointer is at the end of the
	    sequence.  Despite its name,  this  function  does  NOT
	    move the get pointer.
	*/
	int        sgetc ();

	/** This function gets the next  len_ characters  following
	    the get pointer, copying them to the char array pointed
	    to by b_; it advances the get pointer  past  the  last
	    character  fetched.   If  fewer than len characters are
	    left, it gets as many as are available.  
	    @return the number of characters fetched.
	*/
	int        sgetn (char* b_, int len_);

	/** This function stores c just after the put pointer,  and
	    advances  the  pointer one position, possibly extending
	    the sequence.  It returns c, or  EOF  on  error.   What
	    constitutes  an  error  depends  on  the actual derived
	    buffer class.
	*/
	int        sputc (char c_);

	/** From the location pointed to by ptr, stores exactly len
	    characters  after  the  put  pointer, advancing the put
	    pointer just past the last character.  It  returns  the
	    number  of  characters  stored,  which ought to be len.
	    Fewer than len characters stored indicates some sort of
	    error.
	*/
	int        sputn (char* b_, int len_);


	/** If i_ is non-zero, then all IO operations are buffered.
	    If i_ is zero, then unbuffered IO is performed (one
	    character at a time.
	*/
	void       unbuffered (int i_);
	
	/** @return true if unbuffered, false otherwise
	 */
	int        unbuffered ();

protected:
	/** The default constructor is protected for class Streambuf
	    to asssure that only objects for classes derived from this
	    class may be constructed.
	*/
	Streambuf ();

	Streambuf (const Streambuf&);
	Streambuf& operator= (const Streambuf&);

	/** Returns the lowest  possible  value  for  gptr() - the
	    beginning of the get area.
	*/
	char*      base () const;

	/** Returns a pointer to the beginning of the get area, and
	    thus  to the next character to be fetched (if there are
	    any).  The characters immediately  available  are  from
	    gptr() through egptr()-1.  If egptr()<=gptr(), no char-
	    acters are available.
	*/
	char*      gptr ()  const;

	/** Returns a pointer just past the end of  the  get  area,
	    the maximum possible value for gptr(). 
	*/
	char*      egptr () const;

	/** Set get area pointers.
	 */
	void       setg (char* gbeg_, char* gnext_, char* gend_);

	/** Returns a pointer to the beginning fo the space available
	    for the put area, the lowest possible value for pptr().
	    The area from pbase() through pptr()-1 represents characters
	    which have been stored int the buffer but not yet consumed.
	*/
	char*      pbase () const;

	/** Returns a pointer to the beginning of the put area, and
	    thus  to  the  location  of  the next character that is
	    stored (if possible).
	*/
	char*      pptr ()  const;

	/** Returns a pointer just past the end of  the  put  area,
	    the  maximum possible value for pptr().  The space from
	    pptr() through epptr()  is  immediately  available  for
	    storing characters without a flush operation.
	*/
	char*      epptr () const;

	/** Set put area pointers.
	*/
	void       setp (char* pbeg_, char* pend_);

	/** Advances the next pointer for the output sequence by n_.
	 */
	void       pbump (int n_);

	/** Establish the reserve area (buffer). Set base() to b_,
	    ebuf() to eb_. If del_ is non-zero, the buffer will be
	    deleted whenever base() is changed by another call to
	    setb(), or when Streambuf destructor is invoked. If del_
	    is zero, the buffer will not be deleted automatically.
	    @param b_ pointer to the buffer's first byte
	    @param eb_ pointer to the byte one past the buffer's last byte
	    @param del_ 0 - external memory management, 
	    1 - delete on swap/destruction
	*/
	void       setb (char* b_, char* eb_, int del_);

	void       init ();

protected:
	/** Performs  an  operation  that  is  defined separately for 
	    each class derived from Streambuf.
	    Default behavior is to set internal buffer to p_.
	    If p_ is NULL or len_ is 0, then unbuffered I/O (one byte
	    at a time) is assumed.
	    @param p_ buffer to use
	    @param len_ length of the buffer
	*/
	virtual Streambuf* setbuf (char* p_, int len_);

	/** This  function  synchronizes  the  streambuf  with  its
	    actual stream of characters.  The derived class version
	    should flush any characters in the put  area  to  their
	    final  destination, and if possible give back any 
	    characters in the input buffer to their source.  It  should
	    return  EOF on any error, zero on success.  The default
	    behavior of the base class version is to return zero if
	    there   are  no  pending  input  or  output  characters
	    (in_avail()  and  out_waiting()  are  both  zero),  and
	    return EOF otherwise.
	*/
	virtual int        sync ();
	
	/** The morphemes of showmanyc are "es-how-many-see",  
	    not  "show-man-ic".
	    Return an estimate  of the number of characters available in 
	    the sequence, or -1. If it returns a positive value,  
	    then  successive  calls  to underflow() will not return 
	    EOF until at least that number of characters have been supplied.  
	    If showmanyc() returns  -1,  then  calls to underflow() or 
	    uflow() will fail. The intention is not only that these calls 
	    will  not  return  EOF, but that they will return ``immediately.''
	*/
 	virtual int        showmanyc ();

	/** Assigns up to len_ characters to successive elements of the
	    array whose first element is designated by b_. The characters
	    assigned are read from the input sequence as if by repeated calls
	    to sbumpc(). Assigning stops when either len_ characters have 
	    been assigned or a call to sbumpc() would return EOF.
	    @return The number of characters assigned.
	*/
	virtual int        xsgetn (char* b_, int len_);

	/** This function is called to supply characters for  input
	    (from some source) when the get area is empty, although
	    it may be called at other times.  If the  get  area  is
	    not  empty,  it  should just return the first character
	    (without advancing the get pointer).  If the  get  area
	    is  empty,  it  should establish a new get area, aquire
	    new input, and return the first character, if any.   If
	    no  input  characters are available, it should leave an
	    empty get area and return EOF.  The default behavior of
	    the  base  class  version is undefined, so each derived
	    class must define its own underflow.
	*/
	virtual int        underflow ();

	/** Reads the characters from the input sequence, if possible,
	    and moves the stream position past it, as follows:

	    1) If the input sequence has a read position available
	    the function signals success by returning (unsigned char)*gnext++.

	    2) Otherwise, if the function can read the character x
	    directly from the associated input sequence, it signals succes by
	    returning (unsigned char) x. If the function makes a read
	    position available, it also assigns x to *gnext.

	    The default behavior is to call underflow () and, if that
	    function returns EOF or fails to make a read position
	    available, return EOF. Otherwise, the function signals 
	    success by returning (unsigned char)*gnext++.
	*/
	virtual int        uflow ();

	/** Writes up to len_ characters to the output sequence as if
	    by repeated calls to sputc (c). The characters written
	    are obtained from successive elements of the array whose
	    first element is designated by b_. Writing stops when
	    either len_ characters have been written or a call to
	    sputc(c) would return EOF. 
	    @return The number of characters written.
	*/
	virtual int        xsputn (const char* b_, int len_);

	/** This function is called to  consume  characters  (flush
	    them  to  output),  typically when the put area is full
	    and an attempt is made to store another character.   If
	    c is not EOF, overflow must either store or consume the
	    character, following those already in the put area.  It
	    returns EOF on error, any other value on success.
	    The default behavior of the base class version is 
	    undefined,  so each derived class must define its own 
	    overflow.  The normal action for a derived class version is
	    to  consume  the  characters  in  the  put  area (those
	    between pbase() and pptr()), call setp() to  set  up  a
	    new put area, then store c (using sputc()) if it is not
	    EOF.
	*/
	virtual int        overflow (int c = EOF);

	/** This function is called by allocate  when  unbuffered()
	    is  zero  and  base()  is  zero.  It attempts to make a
	    buffer of suitable size available.  On success it  must
	    call  setb to establish the reserve area, then return a
	    value greater than zero.  On failure  it  returns  EOF.
	    The default behavior is to allocate a buffer using new.
	 */
	virtual int        doallocate ();
};

inline Streambuf* 
Streambuf::
pubsetbuf (char* s_, int n_) 
{ 
	trace_with_mask("Streambuf::pubsetbuf",STRMBUFTRACE);
	
	return setbuf (s_, n_); 
}

inline int        
Streambuf::
pubsync () 
{ 
	trace_with_mask("Streambuf::pubsync",STRMBUFTRACE);

	return sync (); 
}

inline int
Streambuf::
in_avail () 
{ 
	trace_with_mask("Streambuf::in_avail",STRMBUFTRACE);

	return m_read_end - m_read_ptr; 
}

inline int
Streambuf::
unbuffered () 
{ 
	trace_with_mask("Streambuf::unbuffered",STRMBUFTRACE);
	
	return m_flags & UNBUFFERED ? 1 : 0; 
}

inline void
Streambuf::
unbuffered (int i_)
{
	trace_with_mask("Streambuf::unbuffered",STRMBUFTRACE);

	if (i_) 
		m_flags |= UNBUFFERED; // set
	else
		m_flags &= ~ UNBUFFERED; // unset
}

inline
Streambuf::
Streambuf () 
{ 
	trace_with_mask("Streambuf::Streambuf",STRMBUFTRACE);

	init (); 
}

inline void
Streambuf::
init ()
{
	trace_with_mask("Streambuf::init", STRMBUFTRACE);

	m_read_base = m_read_ptr = m_read_end = 0;
	m_write_base = m_write_ptr = m_write_end = 0;
	m_buf_base = m_buf_end = 0;
	m_flags = 0;

	m_shortbuf[0] = 0;
}

inline 
Streambuf::
~Streambuf ()
{
	trace_with_mask("Streambuf::~Streambuf",STRMBUFTRACE);

	if (!(m_flags & USER_BUF)) {
		delete [] m_buf_base;
		m_buf_base = m_buf_end = 0;
	}
}

inline char*
Streambuf::
base () const 
{ 
	trace_with_mask("Streambuf::base",STRMBUFTRACE);

	return m_read_base; 
}

inline char* 
Streambuf::
gptr ()  const 
{ 
	trace_with_mask("Streambuf::gptr",STRMBUFTRACE);

	return m_read_ptr; 
}

inline char*
Streambuf::
egptr () const 
{ 
	trace_with_mask("Streambuf::egptr",STRMBUFTRACE);

	return m_read_end; 
}

inline char*      
Streambuf::
pbase () const 
{ 
	trace_with_mask("Streambuf::pbase",STRMBUFTRACE);
	return m_write_base; 
}

inline char*
Streambuf::
pptr ()  const 
{ 
	trace_with_mask("Streambuf::pptr",STRMBUFTRACE);
	return m_write_ptr; 
}

inline char*
Streambuf::
epptr () const 
{ 
	trace_with_mask("Streambuf::epptr",STRMBUFTRACE);
	return m_write_end; 
}

inline void       
Streambuf::
pbump (int n_) 
{ 
	trace_with_mask("Streambuf::pbump",STRMBUFTRACE);
	m_write_ptr += n_; 
}

inline int        
Streambuf::
sync () 
{ 
	trace_with_mask("Streambuf::sync",STRMBUFTRACE);
	return 0; 
}

inline int
Streambuf::
showmanyc () 
{ 
	trace_with_mask("Streambuf::showmanyc",STRMBUFTRACE);
	return -1; 
}

inline int 
Streambuf::
sbumpc ()
{
	trace_with_mask("Streambuf::sbumpc",STRMBUFTRACE);

	return (m_read_ptr >= m_read_end ? uflow ()
			: *(unsigned char *) m_read_ptr++);
}

inline int
Streambuf::
sgetc ()
{
	trace_with_mask("Streambuf::sgetc",STRMBUFTRACE);

	return (m_read_ptr >= m_read_end && underflow () == EOF 
			? EOF :	*(unsigned char*) m_read_ptr);
}

inline int 
Streambuf::
sgetn (char* data_, int len_)
{
	trace_with_mask("Streambuf::sgetn",STRMBUFTRACE);

	return xsgetn (data_, len_);
}

inline int 
Streambuf::
sputc (char c_)
{
	trace_with_mask("Streambuf::sputc",STRMBUFTRACE);

	return (m_write_ptr >= m_write_end
			? overflow (c_)
			: (unsigned char) (*m_write_ptr++ = c_));
}

inline int
Streambuf::
sputn (char* b_, int len_)
{
	trace_with_mask("Streambuf::sputn",STRMBUFTRACE);

	return xsputn (b_, len_);
}

inline void
Streambuf::
setp (char* pbeg_, char* pend_)
{
	trace_with_mask("Streambuf::setp",STRMBUFTRACE);

	m_write_base = m_write_ptr = pbeg_;
	m_write_end  = pend_;
}

inline int
Streambuf::
underflow ()
{
	trace_with_mask("Streambuf::underflow",STRMBUFTRACE);

	return (EOF);
}

inline int
Streambuf::
overflow (int /* c_ */)
{
	trace_with_mask("Streambuf::overflow",STRMBUFTRACE);

	return (EOF);
}

} // end namespace ASSA

#endif /* STREAM_BUF_H */  
