510 lines
14 KiB
C++
510 lines
14 KiB
C++
/************************************************************************
|
|
*
|
|
* rw_streambuf.h - definition of the MyStreambuf class template
|
|
*
|
|
* $Id: rw_streambuf.h 590052 2007-10-30 12:44:14Z faridz $
|
|
*
|
|
***************************************************************************
|
|
*
|
|
* 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
|