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 */