diff options
Diffstat (limited to 'ACE/ace/IOStream.cpp')
-rw-r--r-- | ACE/ace/IOStream.cpp | 665 |
1 files changed, 665 insertions, 0 deletions
diff --git a/ACE/ace/IOStream.cpp b/ACE/ace/IOStream.cpp new file mode 100644 index 00000000000..23790425eeb --- /dev/null +++ b/ACE/ace/IOStream.cpp @@ -0,0 +1,665 @@ +// $Id$ + +#ifndef ACE_IOSTREAM_CPP +#define ACE_IOSTREAM_CPP + +#include "ace/IOStream.h" + +ACE_RCSID(ace, IOStream, "$Id$") + +#if !defined (ACE_LACKS_ACE_IOSTREAM) + +# include "ace/OS_NS_errno.h" +# include "ace/OS_Memory.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. */ + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL + +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_ = 0; + + 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_ (0) +{ + (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 0; + 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 0; + 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; +} + +ACE_END_VERSIONED_NAMESPACE_DECL + +#endif /* !ACE_LACKS_ACE_IOSTREAM */ +#endif /* ACE_IOSTREAM_CPP */ |