| /************************************************************************ |
| * |
| * rw_streambuf.h - definition of the MyStreambuf class template |
| * |
| * $Id$ |
| * |
| *************************************************************************** |
| * |
| * Copyright 2006 The Apache Software Foundation or its licensors, |
| * as applicable. |
| * |
| * Copyright 2004-2006 Rogue Wave Software. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| **************************************************************************/ |
| |
| #ifndef RW_STREAMBUF_H_INCLUDED |
| #define RW_STREAMBUF_H_INCLUDED |
| |
| |
| #include <cstring> // for memset() |
| #include <streambuf> // for basic_streambuf |
| |
| #include <testdefs.h> |
| #include <rw_char.h> // for make_char() |
| #include <rw_exception.h> // for rw_throw() |
| |
| |
| enum MemFun { |
| // bitmask with a bit for each virtual member function |
| None = 0, |
| Setbuf = 0x0001, |
| Seekoff = 0x0002, |
| Seekpos = 0x0004, |
| Showmanyc = 0x0008, |
| Xsgetn = 0x0010, |
| Underflow = 0x0020, |
| Uflow = 0x0040, |
| Overflow = 0x0080, |
| Pbackfail = 0x0100, |
| Xsputn = 0x0200, |
| Sync = 0x0400, |
| // bit OR-ed with MemFun bits |
| Throw = 0x1000, |
| Failure = 0x2000 |
| }; |
| |
| static const char* const streambuf_func_names[] = { |
| "setbuf", "seekoff", "seekpos", "showmanyc", "xsgetn", "underflow", |
| "uflow", "overflow", "pbackfail", "xsputn", "sync" |
| }; |
| |
| template <class charT, class Traits> |
| struct MyStreambuf: std::basic_streambuf<charT, Traits> |
| { |
| typedef charT char_type; |
| typedef Traits traits_type; |
| typedef std::basic_streambuf<char_type, traits_type> Base; |
| typedef typename Base::int_type int_type; |
| typedef typename Base::off_type off_type; |
| typedef typename Base::pos_type pos_type; |
| |
| MyStreambuf (std::streamsize, int, int); |
| |
| MyStreambuf (const char*, std::streamsize, int, int); |
| |
| ~MyStreambuf () { |
| delete[] buf_; |
| } |
| |
| // public interface to base class protected members |
| char_type* pubeback () const { |
| return this->eback (); |
| } |
| |
| char_type* pubgptr () const { |
| return this->gptr (); |
| } |
| |
| char_type* pubegptr () const { |
| return this->egptr (); |
| } |
| |
| char_type* pubpbase () const { |
| return this->pbase (); |
| } |
| |
| char_type* pubpptr () const { |
| return this->pptr (); |
| } |
| |
| char_type* pubepptr () const { |
| return this->epptr (); |
| } |
| |
| void pubsetg (charT *beg, charT *cur, charT *end) { |
| this->setg (beg, cur, end); |
| } |
| |
| void pubsetp (charT *beg, charT *cur, charT *end) { |
| this->setp (beg, end); |
| this->pbump (cur - beg); |
| } |
| |
| private: |
| |
| // overridden protected virtual functions |
| virtual Base* setbuf (char_type*, std::streamsize) { |
| return test (Setbuf) ? this : 0; |
| } |
| |
| virtual pos_type |
| seekoff (off_type, std::ios_base::seekdir, std::ios_base::openmode) { |
| test (Seekoff); |
| return pos_type (off_type (-1)); |
| } |
| |
| virtual pos_type |
| seekpos (pos_type, std::ios_base::openmode) { |
| test (Seekpos); |
| return pos_type (off_type (-1)); |
| } |
| |
| virtual std::streamsize showmanyc () { |
| test (Showmanyc); |
| return 0; |
| } |
| |
| virtual std::streamsize xsgetn (char_type*, std::streamsize) { |
| test (Xsgetn); |
| return 0; |
| } |
| |
| virtual int_type underflow (); |
| |
| virtual int_type uflow (); |
| |
| virtual int_type |
| overflow (int_type = traits_type::eof ()); |
| |
| virtual int_type |
| pbackfail (int_type = traits_type::eof ()); |
| |
| virtual std::streamsize |
| xsputn (const char_type *buf, std::streamsize bufsize) { |
| if (!test (Xsputn)) |
| return 0; |
| return Base::xsputn (buf, bufsize); |
| } |
| |
| virtual int sync () { |
| test (Sync); |
| return 0; |
| } |
| |
| public: |
| |
| int ncalls (MemFun) const; |
| int memfun_inx (MemFun) const; |
| |
| char_type *buf_; |
| std::streamsize bufsize_; |
| |
| int throw_set_; // functions that should throw |
| int fail_set_; // functions that should fail |
| MemFun threw_; // which function threw |
| MemFun failed_; // which function failed |
| |
| int fail_when_; // call number on which to fail |
| |
| int throw_when_ [11]; // call number on which to throw for each func |
| int allthrows_; // total number of thrown exceptions |
| |
| // max size of the pending input sequence |
| static std::streamsize in_pending_; |
| |
| // max size of the pending output sequence |
| static std::streamsize out_pending_; |
| |
| private: |
| |
| bool test (MemFun) const; |
| |
| int ncalls_ [11]; // number of calls made to each function |
| |
| int allcalls_; // total number of calls |
| }; |
| |
| |
| template <class charT, class Traits> |
| std::streamsize MyStreambuf<charT, Traits>:: |
| in_pending_ = 1; |
| |
| |
| template <class charT, class Traits> |
| std::streamsize MyStreambuf<charT, Traits>:: |
| out_pending_ = 1; |
| |
| |
| template <class charT, class Traits> |
| MyStreambuf<charT, Traits>:: |
| MyStreambuf (std::streamsize bufsize, int fail_set, int when) |
| : Base (), buf_ (0), bufsize_ (bufsize), |
| throw_set_ (0), fail_set_ (0), threw_ (None), failed_ (None), |
| fail_when_ (when), allthrows_ (0), allcalls_ (0) |
| { |
| // reset the member function call counters |
| std::memset (ncalls_, 0, sizeof ncalls_); |
| |
| // reset the member function throw counters |
| std::memset (throw_when_, 0, sizeof throw_when_); |
| |
| // allocate a (possibly wide) character buffer for output |
| buf_ = new charT [bufsize_]; |
| |
| // invalidate the contents of the buffer |
| traits_type::assign (buf_, bufsize_, make_char ('\xfe', buf_)); |
| |
| // set the put area to 0 size to force a call to overflow() |
| // on the first write attempt to the buffer |
| this->setp (buf_, buf_); |
| |
| // set the fail and throw flags |
| if (fail_set & Throw) { |
| throw_set_ = fail_set & ~Throw; |
| |
| for (unsigned i = 0; i < 11; ++i) |
| if (throw_set_ & (1U << i)) |
| throw_when_ [i] = when; |
| } |
| else { |
| fail_set_ = fail_set; |
| } |
| } |
| |
| |
| template <class charT, class Traits> |
| MyStreambuf<charT, Traits>:: |
| MyStreambuf (const char *buf, std::streamsize bufsize, int fail_set, int when) |
| : Base (), buf_ (0), bufsize_ (bufsize), |
| throw_set_ (0), fail_set_ (0), threw_ (None), failed_ (None), |
| fail_when_ (when), allthrows_ (0), allcalls_ (0) |
| { |
| // reset the member function call counters |
| std::memset (ncalls_, 0, sizeof ncalls_); |
| |
| // reset the member function throw counters |
| std::memset (throw_when_, 0, sizeof throw_when_); |
| |
| // as a convenience, if `bufsize == -1' compute the size |
| // from the length of `buf' |
| if (std::streamsize (-1) == bufsize_) |
| bufsize_ = std::streamsize (std::char_traits<char>::length (buf)) + 1; |
| |
| // allocate a (possibly wide) character buffer to copy |
| // (and widen) the contents of `buf' into |
| buf_ = new charT [bufsize_ + 1]; |
| |
| for (std::streamsize inx = 0; inx != bufsize_; ++inx) { |
| typedef unsigned char UChar; |
| |
| buf_ [inx] = make_char (buf [inx], buf_); |
| } |
| |
| // zero out the (non-dereferenceable) element just past the end |
| // so that the buffer can be printed out as an ordinary string |
| buf_ [bufsize_] = charT (); |
| |
| // set the get area to 0 size to force a call to underflow() |
| // on the first read attempt from the buffer |
| this->setg (buf_, buf_, buf_); |
| |
| // set the fail and throw flags |
| if (fail_set & Throw) { |
| throw_set_ = fail_set & ~Throw; |
| |
| for (unsigned i = 0; i < 11; ++i) |
| if (throw_set_ & (1U << i)) |
| throw_when_ [i] = when; |
| } |
| else { |
| fail_set_ = fail_set; |
| } |
| } |
| |
| |
| template <class charT, class Traits> |
| typename MyStreambuf<charT, Traits>::int_type |
| MyStreambuf<charT, Traits>:: |
| underflow () |
| { |
| if (!test (Underflow)) |
| return traits_type::eof (); |
| |
| if (this->egptr () - this->gptr () > 0) { |
| this->gbump (1); |
| return traits_type::to_int_type (*this->gptr ()); |
| } |
| |
| if (this->egptr () < buf_ + bufsize_) { |
| |
| // increase the pending input sequence by no more than |
| // the lesser of the available buffer space and `in_pending_' |
| std::streamsize pending = buf_ + bufsize_ - this->egptr (); |
| |
| if (pending > in_pending_) |
| pending = in_pending_; |
| |
| this->setg (this->eback (), this->gptr (), this->egptr () + pending); |
| return traits_type::to_int_type (*this->gptr ()); |
| } |
| |
| failed_ = Underflow; |
| return traits_type::eof (); |
| } |
| |
| |
| template <class charT, class Traits> |
| typename MyStreambuf<charT, Traits>::int_type |
| MyStreambuf<charT, Traits>:: |
| overflow (int_type c /* = traits_type::eof () */) |
| { |
| if (!test (Overflow)) |
| return traits_type::eof (); |
| |
| if (this->epptr () - this->pptr () > 0) { |
| traits_type::assign (*this->pptr (), traits_type::to_char_type (c)); |
| this->pbump (1); |
| return traits_type::not_eof (c); |
| } |
| |
| if (this->epptr () < buf_ + bufsize_) { |
| |
| // increase the pending output sequence by no more than |
| // the lesser of the available buffer space and `out_pending_' |
| std::streamsize pending = buf_ + bufsize_ - this->epptr (); |
| |
| if (pending > out_pending_) |
| pending = out_pending_; |
| |
| const std::streamsize pptr_off = this->pptr () - this->pbase (); |
| |
| this->setp (this->pbase (), this->epptr () + pending); |
| |
| this->pbump (pptr_off); |
| |
| traits_type::assign (*this->pptr (), traits_type::to_char_type (c)); |
| |
| this->pbump (1); |
| |
| return traits_type::not_eof (c); |
| } |
| |
| failed_ = Overflow; |
| return traits_type::eof (); |
| } |
| |
| |
| template <class charT, class Traits> |
| typename MyStreambuf<charT, Traits>::int_type |
| MyStreambuf<charT, Traits>:: |
| pbackfail (int_type c /* = traits_type::eof () */) |
| { |
| if (!test (Pbackfail)) |
| return traits_type::eof (); |
| |
| if (this->gptr () == buf_) { |
| failed_ = Pbackfail; |
| return traits_type::eof (); |
| } |
| |
| this->setg (this->gptr () - 1, this->gptr () - 1, this->gptr ()); |
| |
| const int_type last = traits_type::to_int_type (*this->gptr ()); |
| |
| if (!traits_type::eq_int_type (c, traits_type::eof ())) |
| traits_type::assign (*this->gptr (), traits_type::to_char_type (c)); |
| |
| return last; |
| } |
| |
| |
| template <class charT, class Traits> |
| typename MyStreambuf<charT, Traits>::int_type |
| MyStreambuf<charT, Traits>:: |
| uflow () |
| { |
| if (!test (Uflow)) |
| return traits_type::eof (); |
| |
| if (this->egptr () - this->gptr () > 0) { |
| this->gbump (1); |
| return traits_type::to_int_type (*this->gptr ()); |
| } |
| |
| if (this->egptr () < buf_ + bufsize_) { |
| |
| // increase the pending input sequence by no more than |
| // the lesser of the available buffer space and `in_pending_' |
| std::streamsize pending = buf_ + bufsize_ - this->egptr (); |
| |
| if (pending > in_pending_) |
| pending = in_pending_; |
| |
| this->setg (this->eback (), this->gptr (), this->egptr () + pending); |
| return traits_type::to_int_type (*this->gptr ()); |
| } |
| |
| failed_ = Uflow; |
| return traits_type::eof (); |
| } |
| |
| template <class charT, class Traits> |
| int |
| MyStreambuf<charT, Traits>:: |
| memfun_inx (MemFun which) const |
| { |
| int inx = -1; |
| |
| for (unsigned i = 0; i < sizeof (which) * _RWSTD_CHAR_BIT; ++i) { |
| if (which & (1U << i)) { |
| if (inx < 0) |
| inx = i; |
| else |
| return -1; |
| } |
| } |
| |
| return inx; |
| } |
| |
| template <class charT, class Traits> |
| int |
| MyStreambuf<charT, Traits>:: |
| ncalls (MemFun which) const |
| { |
| int inx = memfun_inx (which); |
| if (0 <= inx) |
| return ncalls_ [inx]; |
| |
| return -1; |
| } |
| |
| |
| template <class charT, class Traits> |
| bool |
| MyStreambuf<charT, Traits>:: |
| test (MemFun which) const |
| { |
| MyStreambuf* const self = _RWSTD_CONST_CAST (MyStreambuf*, this); |
| |
| int inx = memfun_inx (which); |
| if (-1 == inx) |
| return true; |
| |
| // increment the counter tracking the number of calls made |
| // to each member function; do so regardless of whether |
| // an exception will be thrown below |
| self->ncalls_ [inx] ++; |
| self->allcalls_ ++; |
| const int callno = self->ncalls_ [inx]; |
| |
| #ifndef _RWSTD_NO_EXCEPTIONS |
| |
| // if the call counter is equal to the `fail_when_' watermark |
| // and `shich' is set in the `throw_set_' bitmask, throw an |
| // exception with the value of the member id |
| if (callno == throw_when_ [inx] && throw_set_ & which) { |
| self->threw_ = which; |
| self->allthrows_++; |
| |
| rw_throw (ex_stream, __FILE__, __LINE__, |
| streambuf_func_names [inx], |
| "%s", "test exception"); |
| } |
| |
| #else // if defined (_RWSTD_NO_EXCEPTIONS) |
| |
| if (callno == throw_when_ [inx] && throw_set_ & which) { |
| self->threw_ = which; |
| return false; |
| } |
| |
| #endif // _RWSTD_NO_EXCEPTIONS |
| |
| // ...otherwise check if the member should succeed or fail |
| // and return true or false, respectively |
| const bool success = !(fail_set_ & which) || callno != fail_when_; |
| |
| if (!success) |
| self->failed_ = which; |
| |
| return success; |
| } |
| |
| |
| #endif // RW_STREAMBUF_H_INCLUDED |