/home/aherms/Code/git/awds-routing/src/unixfdostream.h

00001 // Copyright 2005, Chris Frey.  To God be the glory.
00002 // You are free to use, modify, redistribute, and sublicense this code,
00003 // as long as this copyright message is not removed from the source,
00004 // and as long as the existence of any changes are noted in the source
00005 // as well.  (i.e. You don't need a complete history, just don't claim
00006 // that the modified code was written entirely by me -- include your own
00007 // copyright notice as well.)
00008 //
00009 // If you find this code useful, please email me and let me know.
00010 //
00011 // Conclusion:
00012 //      C++'s getline() processes the stream one character at a time, in order
00013 //      to search for the delimiter.  Therefore, with networkbuf, which only
00014 //      fills the input buffer as much as it can with the available network
00015 //      data and then returns, it doesn't block until it really needs to,
00016 //      and gets all available lines from the kernel, even with a nice large
00017 //      buffer.
00018 //
00019 //      With stdio_filebuf, this is built on top of C's streams, which has its
00020 //      own buffer.  So the likelyhood of blocking before getting all the
00021 //      data is higher, since C's buffers may empty and get forced to fill
00022 //      with an internal fread() while C++'s getline is only asking for a
00023 //      single char.
00024 //
00025 //      C++ streambufs and derived classes are actually pretty cool.  I just
00026 //      wish they had used more user-friendly names.  But as is, you can
00027 //      still use functions like xsgetn and xsputn as read and write, and
00028 //      the streambuf supplies all the needed buffering.  Plus you can do it
00029 //      on a character basis, while still maintaining efficiency with
00030 //      buffered kernel calls.  Plus there are iterators to work with these
00031 //      things, which I haven't fully investigated.
00032 //
00033 //      Time to rethink my design of reuse lib's buffer classes, and turn
00034 //      them into streambufs perhaps, or at least derive streambuf interface
00035 //      classes to make use of them.  Also, the transfer classes should be
00036 //      able to use streambufs as well.
00037 //
00038 //      This stuff is complicated, and not commonly well documented, but
00039 //      it sure is useful.
00040 //
00041 // Chris Frey
00042 // <cdfrey@netdirect.ca>
00043 // 2005/02/13
00044 //
00045 
00046 #include <cassert>
00047 #include <iostream>
00048 
00049 class UnixFdStreamBuf : public  std::basic_streambuf<char> {
00050   
00051     static const size_t bufSize = 256;
00052     char buffer[bufSize + 1]; // one additional character for  overflow ...
00053   
00054     const int fd;
00055   
00056 public:
00057     UnixFdStreamBuf(int fd) : std::basic_streambuf<char>(),
00058                               fd(fd)
00059     {
00060         setp(buffer, buffer + bufSize);
00061     };
00062   
00063     // write out any data in the out buffer, and write c as well if c!=eof()
00064     virtual int_type overflow (int_type c) {
00065         //              cout << "overflow called: " << c << endl;
00066         assert( pbase() );
00067         bool have_extra = c != traits_type::eof();
00068 
00069         // pbase() is a pointer at the start of our buffer,
00070         // and pptr() is a pointer to the next free spot,
00071         // so we can check for data in buffer by comparing them
00072         if( pptr() > pbase() || have_extra ) {
00073             // we have something to write!
00074             ssize_t count = pptr() - pbase();
00075             if( have_extra ) {
00076                 // tack extra value onto the end of the buf
00077                 // (note constructor added a byte for us here,
00078                 // just in case)
00079                 *(pptr()) = traits_type::to_char_type(c);
00080                 count++;
00081             }
00082 
00083             char_type *wp = pbase();
00084             
00085             if ( ! writeSomeBytes(wp, count) )
00086                 return traits_type::eof();
00087             
00088             // reset output buffer to empty state
00089             setp(buffer, buffer+bufSize);
00090         }
00091         return traits_type::not_eof(c);
00092     }
00093 
00094   
00095     virtual int sync() {
00096         if( overflow(traits_type::eof()) == traits_type::eof() )
00097             return -1;
00098         return 0;
00099     }
00100   
00101 protected:
00102     
00103     virtual bool writeSomeBytes(const char *data, int count) {
00104         int ret;
00105         
00106         do {
00107             do {
00108             ret = ::write( this->fd, data, count);
00109             } while (ret == -1 && errno == EINTR);
00110             
00111             if( ret > 0 ) {
00112                 count -= ret;
00113                 assert(count >= 0);
00114                 data += ret;
00115             }
00116             else {
00117                 // fixme... do we set badbit here?
00118                 // failbit?
00119                 return false;
00120             }
00121         } while(count);
00122 
00123         return true;
00124     }
00125 };
00126 
00127 
00128 
00129 /* This stuff is for emacs
00130  * Local variables:
00131  * mode:c++
00132  * c-basic-offset: 4
00133  * End:
00134  */

Generated on Tue Dec 11 17:58:48 2007 for AWDS by  doxygen 1.5.3-20071008