summaryrefslogtreecommitdiff
path: root/ace/IOStream.cpp
diff options
context:
space:
mode:
authorschmidt <douglascraigschmidt@users.noreply.github.com>1997-02-05 05:01:13 +0000
committerschmidt <douglascraigschmidt@users.noreply.github.com>1997-02-05 05:01:13 +0000
commite589686a7e0687a5713b8495c74e83a9752e57c4 (patch)
treee502ccaf24f415d15f301840b4d384145a93d2cb /ace/IOStream.cpp
parentf18e114a04aa1dc6412b16aae0d59141d9c863db (diff)
downloadATCD-e589686a7e0687a5713b8495c74e83a9752e57c4.tar.gz
foo
Diffstat (limited to 'ace/IOStream.cpp')
-rw-r--r--ace/IOStream.cpp586
1 files changed, 586 insertions, 0 deletions
diff --git a/ace/IOStream.cpp b/ace/IOStream.cpp
new file mode 100644
index 00000000000..69b50bb36de
--- /dev/null
+++ b/ace/IOStream.cpp
@@ -0,0 +1,586 @@
+// IOStream.cpp
+// $Id$
+#if !defined (ACE_IOSTREAM_C)
+#define ACE_MAP_IOSTREAM_C
+
+#define ACE_BUILD_DLL
+#include "ace/IOStream.h"
+
+/* 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. */
+
+template <class STREAM> int
+ACE_Streambuf<STREAM>::underflow( void )
+{
+ // If input mode is not set, any attempt to read from the stream is
+ // a failure.
+
+ if (!(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_ + ACE_STREAMBUF_SIZE, 0);
+
+ // 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);
+
+ // 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_;
+ }
+ 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;
+
+ // We're about to disable put mode but before we do that, we
+ // wan't 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_ + ACE_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.
+
+template <class STREAM> int
+ACE_Streambuf<STREAM>::overflow (int c)
+{
+ // Check to see if output is allowed at all.
+ if (!(mode_ & ios::out))
+ return EOF;
+
+ // First invokation of a get or put function
+ if (!base ())
+ {
+ // Set base () to use put's private buffer.
+ //
+ setb (this->pbase_saved_,
+ this->pbase_saved_ + ACE_STREAMBUF_SIZE, 0);
+
+ // Set the put area using the new base () values.
+ setp (base (), ebuf ());
+
+ // Disable the get area.
+ setg (0, 0, 0);
+
+ // Set the mode for optimization.
+ this->cur_mode_ = this->put_mode_;
+ }
+ else // We're already reading or writting
+ {
+ // If we're coming out of get mode...
+ if (this->cur_mode_ == this->get_mode_)
+ {
+ // Save the current get mode values
+ this->eback_saved_ = eback ();
+ this->gptr_saved_ = gptr ();
+ this->egptr_saved_ = egptr ();
+
+ // then disable the get buffer
+ setg (0, 0, 0);
+
+ // Reconfigure base () and restore the put pointers.
+ setb (pbase_saved_, pbase_saved_ + ACE_STREAMBUF_SIZE, 0);
+ setp (pbase_saved_, epptr_saved_);
+
+ // 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 () = c;
+ pbump (1);
+ }
+
+ return 0;
+}
+
+// syncin
+
+template <class STREAM> int
+ACE_Streambuf<STREAM>::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
+
+template <class STREAM> int
+ACE_Streambuf<STREAM>::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;
+}
+
+template <class STREAM> int
+ACE_Streambuf<STREAM>::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 ();
+
+ return 0;
+}
+
+// flushbuf
+
+template <class STREAM> int
+ACE_Streambuf<STREAM>::flushbuf (void)
+{
+ size_t n;
+ char *q;
+
+ // Get a pointer to the beginning of the output buffer
+ q = pbase ();
+
+ // and calculate the number of bytes based on the end-of-data pointer
+ // and the beginning-of-data pointer.
+ n = pptr () - q;
+
+ // Use the send_n function to ensure that everything waiting in the
+ // put area is sent to the peer.
+ ssize_t s = peer_->send_n (q, n);
+
+ // If we didn't send as many as we wanted then there is something
+ // wrong with the peer. A send_n guarantees the delivery of our
+ // data or total failure.
+ if (s < n)
+ return EOF;
+
+ // Now that we've sent everything in the output buffer, we reset the
+ // buffer pointers to appear empty.
+ setp (base (), base ());
+
+ return 0;
+}
+
+template <class STREAM>
+int ACE_Streambuf<STREAM>::get_one_byte (void)
+{
+ char * p = base ();
+ ssize_t i = peer_->recv_n (p, 1);
+
+ // 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 (i != 1)
+ return EOF;
+
+ // Set the get pointers to indicate that we've got new data.
+ setg (base (), base (), base () + 1);
+
+ return 1;
+}
+
+template <class STREAM> int
+ACE_Streambuf<STREAM>::fillbuf (void)
+{
+ char *p;
+
+ // Invoke recv_n to get at least one byte from the remote. This
+ // will block until something shows up.
+
+ if (this->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. Notice that we can only read
+ // blen ()-1 bytes since we've already read one via <get_one_byte>
+
+ p = base () + 1;
+ ssize_t t = peer_->recv (p, blen () - 1);
+
+ // 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 (t++ < 0)
+ return EOF;
+
+ // Move the get pointer to reflect the number of bytes we just read.
+
+ setg (base (), base (), base () + t);
+
+ // Return the byte-read-count including the one from <get_one_byte>
+ return t;
+}
+
+// We will be given a STREAM by the iostream object which creates us.
+// See the ACE_IOStream template for how that works. Like other
+// streambuf objects, we can be input-only, output-only or both.
+
+template <class STREAM>
+ACE_Streambuf<STREAM>::ACE_Streambuf (STREAM *peer, int io_mode)
+ : peer_ (peer),
+ mode_ (io_mode)
+{
+ // A streambuf allows for unbuffered IO where every character is
+ // read as requested and written as provided. To me, this seems
+ // terribly inefficient for socket-type operations, so I've disabled
+ // it. All of the work would be done by the underflow/overflow
+ // functions anyway and I haven't implemented anything there to
+ // support unbuffered IO.
+
+ this->unbuffered (0);
+
+ // Linebuffered is similar to unbuffered. Again, I don't have any
+ // need for this and I don't see the advantage. I believe this
+ // would have to be supported by underflow/overflow to be effective.
+ this->linebuffered (0);
+
+ // The get area is where the iostrem 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.
+ //
+ ACE_NEW (this->eback_saved_, char[ACE_STREAMBUF_SIZE]);
+ this->gptr_saved_ = this->eback_saved_;
+ this->egptr_saved_ = this->eback_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.
+ //
+ ACE_NEW (this->pbase_saved_, char[ACE_STREAMBUF_SIZE]);
+ this->pptr_saved_ = this->pbase_saved_;
+ this->epptr_saved_ = this->pbase_saved_ + ACE_STREAMBUF_SIZE;
+
+ // Disable the get area initially. This will cause underflow to be
+ // invoked on the first get operation.
+ setg (0, 0, 0);
+
+ // Disable the put area. Overflow will be called by the first call
+ // to any put operator.
+ setp (0, 0);
+
+ // 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.
+
+template <class STREAM>
+ACE_Streambuf<STREAM>::~ACE_Streambuf (void)
+{
+ delete [] this->eback_saved_;
+ delete [] this->pbase_saved_;
+}
+
+// The typical constructor. This will initiailze your STREAM and then
+// setup the iostream baseclass to use a custom streambuf based on
+// STREAM.
+
+template <class STREAM>
+ACE_IOStream<STREAM>::ACE_IOStream (void)
+ : iostream (streambuf_ = new ACE_Streambuf<STREAM> ((STREAM *) this))
+{
+}
+
+// We have to get rid of the streambuf_ ourselves since we gave it to
+// iostream ()
+
+template <class STREAM>
+ACE_IOStream<STREAM>::~ACE_IOStream (void)
+{
+ delete this->streambuf_;
+}
+
+// The only ambituity in the multiple inheritance is the close ()
+// function.
+
+template <class STREAM> int
+ACE_IOStream<STREAM>::close (void)
+{
+ return ACE_SOCK_Stream::close ();
+}
+
+#if defined (__GNUC__)
+// A simple string operator. The base iostream has 'em for char* but
+// that isn't always the best thing for a String. If we don't provide
+// our own here, we may not get what we want.
+
+template <class STREAM> ACE_IOStream<STREAM> &
+ACE_IOStream<STREAM>::operator>> (String & v)
+{
+ char c;
+ iostream::operator>> (c);
+
+ for (v = c ; this->get (c) && !isspace (c) ; v += c)
+ continue;
+
+ return *this;
+}
+
+
+//////////////////////////////////////////////////////////////////
+// A more clever put operator for strings that knows how to
+// deal with quoted strings containing back-quoted quotes.
+//
+template <class STREAM> ACE_IOStream<STREAM> &
+ACE_IOStream<STREAM>::operator>> (QuotedString & str)
+{
+ char c;
+
+ if( !(*this >> c) ) // eat space up to the first char
+ {
+ // this->set(ios::eofbit|ios::failbit);
+ return *this;
+ }
+
+ str = ""; // Initialize the string
+
+ // if we don't have a quote, append until we see space
+ if (c != '"')
+ {
+ for( str = c ; this->get(c) && !isspace(c) ; str += c );
+ }
+ else
+ {
+ for( ; this->get(c) && c != '"' ; str += c )
+ {
+ if (c == '\\')
+ {
+ this->get(c);
+ if (c != '"')
+ str += '\\';
+ }
+ }
+ }
+
+ return *this;
+}
+
+template <class STREAM> ACE_IOStream<STREAM> &
+ACE_IOStream<STREAM>::operator<< (QuotedString & str)
+{
+ *this << '"';
+ *this << (String&)str;
+ *this << '"';
+ return *this;
+}
+
+#endif /* __GNUG__ */
+#endif /* ACE_IOSTREAM_C */