// IOStream.cpp,v 4.30 1999/07/03 18:26:16 schmidt Exp

#if !defined (ACE_IOSTREAM_C)
#define ACE_IOSTREAM_C

#define ACE_BUILD_DLL

#include "ace/IOStream.h"

ACE_RCSID(ace, IOStream, "IOStream.cpp,v 4.30 1999/07/03 18:26:16 schmidt Exp")

#if !defined (ACE_LACKS_ACE_IOSTREAM)

///////////////////////////////////////////////////////////////////////////

/* Here's a simple example of how iostream's non-virtual operators can
   get you in a mess:

  class myiostream : public iostream
  {
  public:
          myiostream& operator>> (String & s)
          {
                  ...
          }
  };

  ...

  int i;
  String s;
  myiostream foo (...);

  foo >> s;
  // OK
  // invokes myiostream::operator>> (String&) returning myiostream&

  foo >> i;
  // OK
  // invokes iostream::operator>> (int&) returning iostream&

  foo >> i >> s;
  // BAD
  // invokes iostream::operator>> (int&) then iostream::operator>> (String&)
  //
  // What has happened is that the first >> is invoked on the base class and returns
  // a reference to iostream.  The second >> has no idea of the ACE_IOStream and
  // gets invoked on iostream.  Probably NOT what you wanted!


  // In order to make all of this work the way you want, you have to do this:

  class myiostream : public iostream
  {
  public:
          myiostream& operator>> (int & i)
          {
                  return ((myiostream&)iostream::operator>> (i));
          }

          myiostream& operator>> (String & s)
          {
                  ...
          }
  };

  ...

  int i;
  String s;
  myiostream foo (...);

  foo >> s;
  // OK
  // invokes myiostream::operator>> (String&) returning myiostream&

  foo >> i;
  // OK
  // invokes myiostream::operator>> (int&) returning myiostream&


  foo >> i >> s;
  // OK
  // Because you provided operator>> (int&) in class myiostream, that
  // function will be invoked by the first >>.  Since it returns
  // a myiostream&, the second >> will be invoked as desired.  */

ACE_HANDLE
ACE_Streambuf::get_handle (void)
{
  return 0;
}

ACE_Time_Value *
ACE_Streambuf::recv_timeout (ACE_Time_Value *tv)
{
  ACE_Time_Value * rval = recv_timeout_;
  if (tv)
    {
      recv_timeout_value_ = *tv;
      recv_timeout_ = &recv_timeout_value_;
    }
  else
    recv_timeout_ = NULL;

  return rval;
}

int
ACE_Streambuf::underflow (void)
{
  // If input mode is not set, any attempt to read from the stream is
  // a failure.

  if (ACE_BIT_DISABLED (mode_, ios::in))
    return EOF;

  // If base () is empty then this is the first time any get/put
  // operation has been attempted on the stream.

  if (!this->base ())
    {
      // Set base () to use our private read buffer.  The arguments are:
      //        beginning of the buffer (base ())
      //        one-beyond the end of the buffer (ebase ())
      //        should base () be deleted on destruction
      //
      // We have to say "no" to the third parameter because we want to
      // explicitly handle deletion of the TWO buffers at destruction.

      setb (this->eback_saved_,
            this->eback_saved_ + streambuf_size_, 0);

      // Remember that we are now in getMode.  This will help us if
      // we're called prior to a mode change as well as helping us
      // when the mode does change.
      this->cur_mode_ = this->get_mode_;
      // Using the new values for base (), initialize the get area.
      // This simply sets eback (), gptr () and egptr () described
      // earlier.
      setg (base (), base (), base ());

      // Set the put buffer such that puts will be disabled.  Any
      // attempt to put data will now cause overflow to be invoked.
      setp (0, 0);
    }
  else  // base () has been initialized already...
    {
      // If we are in put_mode_ now, then it is time to switch to get_mode_
      //
      // 1. get rid of any pending output
      // 2. rearrange base () to use our half of the buffer
      // 3. reset the mode
      //
      if (this->cur_mode_ == this->put_mode_)
        {
          // Dump any pending output to the peer.  This is not really
          // necessary because of the dual-buffer arrangement we've
          // set up but intuitively it makes sense to send the pending
          // data before we request data since the peer will probably
          // need what we're sending before it can respond.
          if (out_waiting () && syncout () == EOF)
            return EOF;

          if( ! pbase() )
            {
              delete [] pbase_saved_;
              (void) reset_put_buffer();
            }
          else
            {
              // We're about to disable put mode but before we do
              // that, we want to preserve it's state.
              this->pbase_saved_ = pbase ();
              this->pptr_saved_  = pptr ();
              this->epptr_saved_ = epptr ();
            }

          // Disable put mode as described in the constructor.
          setp (0, 0);

          // Like the case where base () is false, we now point base
          // () to use our private get buffer.
          setb (this->eback_saved_,
                this->eback_saved_ + streambuf_size_,
                0);

          // And restore the previous state of the get pointers.

          setg (this->eback_saved_, this->gptr_saved_,
                this->egptr_saved_);

          // Finally, set our mode so that we don't get back into this
          // if () and so that overflow can operate correctly.
          cur_mode_ = get_mode_;
        }

      // There could be data in the input buffer if we switched to put
      // mode before reading everything.  In that case, we take this
      // opportunity to feed it back to the iostream.
      if (in_avail ())
        // Remember that we return an int so that we can give back
        // EOF.  The explicit cast prevents us from returning a signed
        // char when we're not returning EOF.
        return (u_char) *gptr ();
    }

  // We really shouldn't be here unless there is a lack of data in the
  // read buffer.  So... go get some more data from the peer.

  int result = fillbuf ();

  // Fillbuf will give us EOF if there was an error with the peer.  In
  // that case, we can do no more input.

  if (EOF == result)
    {
      // Disable ourselves and return failure to the iostream.  That
      // should result in a call to have oursleves closed.
      setg (0, 0, 0);
      return EOF;
    }

  // Return the next available character in the input buffer.  Again,
  // we protect against sign extension.

  return (u_char) *gptr ();
}

// Much of this is similar to underflow.  I'll just hit the highlights
// rather than repeating a lot of what you've already seen.

int
ACE_Streambuf::overflow (int c)
{
  // Check to see if output is allowed at all.
  if (! (mode_ & ios::out))
    return EOF;

  if (!base ())
    {
      // Set base () to use put's private buffer.
      //
      setb (this->pbase_saved_,
            this->pbase_saved_ + streambuf_size_, 0);

      // Set the mode for optimization.
      this->cur_mode_ = this->put_mode_;
      // Set the put area using the new base () values.
      setp (base (), ebuf ());

      // Disable the get area.
      setg (0, 0, 0);
    }
  else  // We're already reading or writing
    {
      // If we're coming out of get mode...
      if (this->cur_mode_ == this->get_mode_)
        {
          // --> JCEJ 6/6/98
          if (! eback())
            {
              /* Something has happened to cause the streambuf
                 to get rid of our get area.
                 We could probably do this a bit cleaner but
                 this method is sure to cleanup the bits and
                 pieces.
              */
              delete [] eback_saved_;
              (void) reset_get_buffer();
            }
          else
            {
              // Save the current get mode values
              this->eback_saved_ = eback ();
              this->gptr_saved_  = gptr ();
              this->egptr_saved_ = egptr ();
            }
          // <-- JCEJ 6/6/98

          // then disable the get buffer
          setg (0, 0, 0);

          // Reconfigure base () and restore the put pointers.
          setb (pbase_saved_, pbase_saved_ + streambuf_size_, 0);
          setp (base (), ebuf ());

          // Save the new mode.
          this->cur_mode_ = this->put_mode_;
        }

      // If there is output to be flushed, do so now.  We shouldn't
      // get here unless this is the case...

      if (out_waiting () && EOF == syncout ())
        return EOF;
    }

  // If we're not putting EOF, then we have to deal with the character
  // that is being put.  Perhaps we should do something special with EOF???

  if (c != EOF)
    {
      // We've already written any data that may have been in the
      // buffer, so we're guaranteed to have room in the buffer for
      // this new information.  So... we add it to the buffer and
      // adjust our 'next' pointer acordingly.
      *pptr () = (char) c;
      pbump (1);
    }

  return 0;
}

// syncin

int
ACE_Streambuf::syncin (void)
{
  // As discussed, there really isn't any way to sync input from a
  // socket-like device.  We specifially override this base-class
  // function so that it won't do anything evil to us.
  return 0;
}

// syncout

int
ACE_Streambuf::syncout (void)
{
  // Unlike syncin, syncout is a doable thing.  All we have to do is
  // write whatever is in the output buffer to the peer.  flushbuf ()
  // is how we do it.

  if (flushbuf () == EOF)
    return EOF;
  else
    return 0;
}

int
ACE_Streambuf::sync (void)
{
  // sync () is fairly traditional in that it syncs both input and
  // output.  We could have omitted the call to syncin () but someday,
  // we may want it to do something.

  syncin ();

  // Don't bother syncing the output unless there is data to be
  // sent...

  if (out_waiting ())
    return syncout ();
  else
    return 0;
}

// flushbuf

int
ACE_Streambuf::flushbuf (void)
{
  // pptr () is one character beyond the last character put into the
  // buffer.  pbase () points to the beginning of the put buffer.
  // Unless pptr () is greater than pbase () there is nothing to be
  // sent to the peer.

  if (pptr () <= pbase ())
    return 0;

  // 4/12/97 -- JCEJ
  // Kludge!!!
  // If the remote side shuts down the connection, an attempt to send
  // () to the remote will result in the message 'Broken Pipe' I think
  // this is an OS message, I've tracked it down to the ACE_OS::write
  // () function.  That's the last one to be called before the
  // message.  I can only test this on Linux though, so I don't know
  // how other systems will react.
  //
  // To get around this gracefully, I do a PEEK recv () with an
  // immediate (nearly) timeout.  recv () is much more graceful on
  // it's failure.  If we get -1 from recv () not due to timeout then
  // we know we're SOL.
  //
  // Q:  Is 'errno' threadsafe?  Should the section below be a
  //     critical section?
  //
  // char tbuf[1];
  // ACE_Time_Value to (0,1);
  // if (this->recv (tbuf, 1, MSG_PEEK, &to) == -1)
  // {
  //    if (errno != ETIME)
  //    {
  //            perror ("OOPS preparing to send to peer");
  //            return EOF;
  //    }
  // }
  //
  // The correct way to handle this is for the application to trap
  // (and ignore?) SIGPIPE.  Thanks to Amos Shapira for reminding me
  // of this.

  // Starting at the beginning of the buffer, send as much data as
  // there is waiting.  send guarantees that all of the data will be
  // sent or an error will be returned.

  if (this->send (pbase (), pptr () - pbase ()) == -1)
    return EOF;

  // Now that we've sent everything in the output buffer, we reset the
  // buffer pointers to appear empty.
  setp (base (), ebuf ());

  return 0;
}

int
ACE_Streambuf::get_one_byte (void)
{
  this->timeout_ = 0;

  // The recv function will return immediately if there is no data
  // waiting.  So, we use recv_n to wait for exactly one byte to come
  // from the peer.  Later, we can use recv to see if there is
  // anything else in the buffer. (Ok, we could use flags to tell it
  // to block but I like this better.)

  if (this->recv_n (base (), 1, MSG_PEEK, this->recv_timeout_) != 1)
    {
      if (errno == ETIME)
        this->timeout_ = 1;
      return EOF;
    }
  else
    return 1;
}

// This will be called when the read (get) buffer has been exhausted
// (ie -- gptr == egptr).

int
ACE_Streambuf::fillbuf (void)
{
  // Invoke recv_n to get exactly one byte from the remote.  This will
  // block until something shows up.

  if (get_one_byte () == EOF)
    return EOF;

  // Now, get whatever else may be in the buffer.  This will return if
  // there is nothing in the buffer.

  int bc = this->recv (base (), blen (), this->recv_timeout_);

  // recv will give us -1 if there was a problem.  If there was
  // nothing waiting to be read, it will give us 0.  That isn't an
  // error.

  if (bc < 0)
    {
      if (errno == ETIME)
        this->timeout_ = 1;
      return EOF;
    }

  // Move the get pointer to reflect the number of bytes we just read.

  setg (base (), base (), base () + bc);

  // Return the byte-read-count including the one from <get_one_byte>.
  return bc;
}

ACE_Streambuf::ACE_Streambuf (u_int streambuf_size, int io_mode)
  : eback_saved_ (0),  // to avoid Purify UMR
    pbase_saved_ (0),  // to avoid Purify UMR
    get_mode_ (1),
    put_mode_ (2),
    mode_ (io_mode),
    streambuf_size_ (streambuf_size),
    recv_timeout_ (NULL)
{
 (void)reset_get_buffer ();
 (void)reset_put_buffer ();
}

u_int
ACE_Streambuf::streambuf_size (void)
{
  return streambuf_size_;
}

// Return the number of bytes not yet gotten. eback + get_waiting =
// gptr.

u_int
ACE_Streambuf::get_waiting (void)
{
  return this->gptr_saved_ - this->eback_saved_;
}

// Return the number of bytes in the get area (includes some already
// gotten); eback + get_avail = egptr.

u_int
ACE_Streambuf::get_avail (void)
{
  return this->egptr_saved_ - this->eback_saved_;
}

// Return the number of bytes to be 'put' onto the stream media.
// pbase + put_avail = pptr.

u_int
ACE_Streambuf::put_avail (void)
{
  return this->pptr_saved_ - this->pbase_saved_;
}

// Typical usage:
//
//      u_int  newGptr  = otherStream->get_waiting ();
//      u_int  newEgptr = otherStream->get_avail ();
//      char * newBuf   = otherStream->reset_get_buffer ();
//      char * oldgetbuf = myStream->reset_get_buffer (newBuf, otherStream->streambuf_size (), newGptr, newEgptr);
//
//      'myStream' now has the get buffer of 'otherStream' and can use it in any way.
//      'otherStream' now has a new, empty get buffer.

char *
ACE_Streambuf::reset_get_buffer (char *newBuffer,
                                 u_int _streambuf_size,
                                 u_int _gptr,
                                 u_int _egptr)
{
  char * rval = this->eback_saved_;

  // The get area is where the iostream will get data from.  This is
  // our read buffer.  There are three pointers which describe the
  // read buffer:
  //
  //    eback () - The beginning of the buffer.  Also the furthest
  //              point at which putbacks can be done.  Hence the name.
  //
  //    gptr ()  - Where the next character is to be got from.
  //
  //    egptr () - One position beyond the last get-able character.
  //
  // So that we can switch quicky from read to write mode without
  // any data copying, we keep copies of these three pointers in
  // the variables below.  Initially, they all point to the beginning
  // of our read-dedicated buffer.
  //
  if (newBuffer)
    {
      if (streambuf_size_ != _streambuf_size)
        return NULL;
      this->eback_saved_ = newBuffer;
    }
  else
    ACE_NEW_RETURN (this->eback_saved_,
                    char[streambuf_size_],
                    0);

  this->gptr_saved_ = this->eback_saved_ + _gptr;
  this->egptr_saved_ = this->eback_saved_ + _egptr;

  // Disable the get area initially.  This will cause underflow to be
  // invoked on the first get operation.
  setg (0, 0, 0);

  reset_base ();

  return rval;
}

// Typical usage:
//
//      u_int  newPptr = otherStream->put_avail ();
//      char * newBuf  = otherStream->reset_put_buffer ();
//      char * oldputbuf = otherStream->reset_put_buffer (newBuf, otherStream->streambuf_size (), newPptr);

char *
ACE_Streambuf::reset_put_buffer (char *newBuffer,
                                 u_int _streambuf_size,
                                 u_int _pptr)
{
  char *rval = this->pbase_saved_;

  // The put area is where the iostream will put data that needs to be
  // sent to the peer.  This becomes our write buffer.  The three
  // pointers which maintain this area are:
  //
  //    pbase () - The beginning of the put area.
  //
  //    pptr ()  - Where the next character is to be put.
  //
  //    epptr () - One beyond the last valid position for putting.
  //
  // Again to switch quickly between modes, we keep copies of
  // these three pointers.
  //
  if (newBuffer)
    {
      if (streambuf_size_ != _streambuf_size)
        return NULL;
      this->pbase_saved_ = newBuffer;
    }
  else
    ACE_NEW_RETURN (this->pbase_saved_,
                    char[streambuf_size_],
                    0);

  this->pptr_saved_ = this->pbase_saved_ + _pptr;
  this->epptr_saved_ = this->pbase_saved_ + streambuf_size_;

  // Disable the put area.  Overflow will be called by the first call
  // to any put operator.
  setp (0, 0);

  reset_base ();

  return rval;
}

void
ACE_Streambuf::reset_base (void)
{
  // Until we experience the first get or put operation, we do not
  // know what our current IO mode is.
  this->cur_mode_ = 0;

  // The common area used for reading and writting is called "base".
  // We initialize it this way so that the first get/put operation
  // will have to "allocate" base.  This allocation will set base to
  // the appropriate specific buffer and set the mode to the correct
  // value.
  setb (0, 0);
}

// If the default allocation strategey were used the common buffer
// would be deleted when the object destructs.  Since we are providing
// separate read/write buffers, it is up to us to manage their memory.

ACE_Streambuf::~ACE_Streambuf (void)
{
  delete [] this->eback_saved_;
  delete [] this->pbase_saved_;
}

u_char ACE_Streambuf::timeout (void)
{
  u_char rval = this->timeout_;
  this->timeout_ = 0;
  return rval;
}

#endif /* !ACE_LACKS_ACE_IOSTREAM */
#endif /* ACE_IOSTREAM_C */
