blob: c9aba87b0d48fdf0442fc41e87d3fcec00b52b94 [file] [log] [blame]
/************************************************************************
*
* rw_streambuf.h - definition of the MyStreambuf class template
*
* $Id$
*
***************************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you 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.
*
* Copyright 2004-2006 Rogue Wave Software.
*
**************************************************************************/
#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