// -*- C++ -*- /*****************************************************************************\ * Copyright (c) 2004 Mark Aylett * * * * Permission is hereby granted, free of charge, to any person obtaining a * * copy of this software and associated documentation files (the * * "Software"), to deal in the Software without restriction, including * * without limitation the rights to use, copy, modify, merge, publish, * * distribute, sublicense, and/or sell copies of the Software, and to permit * * persons to whom the Software is furnished to do so, subject to the * * following conditions: * * * * The above copyright notice and this permission notice shall be included * * in all copies or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * * USE OR OTHER DEALINGS IN THE SOFTWARE. * \*****************************************************************************/ /** * \file mar_streambuffer_cpp.h * \brief TODO */ #ifndef INCLUDED_MAR_STREAMBUF_CPP #define INCLUDED_MAR_STREAMBUF_CPP #ifndef INCLUDED_MAR_ARCHIVE_CPP #include "mar_archive_cpp.h" #endif // INCLUDED_MAR_ARCHIVE_CPP #ifndef INCLUDED_MAR_COMPAT_CPP #include "mar_compat_cpp.h" #endif // INCLUDED_MAR_COMPAT_CPP #ifndef INCLUDED_IOSTREAM #define INCLUDED_IOSTREAM #include #endif // INCLUDED_IOSTREAM namespace mar { class state { public: enum which { neutral, get, put }; private: which which_; public: state() : which_(neutral) { } which operator ()(which w) { std::swap(which_, w); return w; } which operator ()() const { return which_; } }; template > class buffer { public: typedef charT char_type; typedef char_traitsT char_traits_type; typedef typename char_traits_type::int_type int_type; typedef typename char_traits_type::pos_type pos_type; typedef typename char_traits_type::off_type off_type; enum { pbacksize = 4 }; private: std::ios_base::openmode openmode_; std::streamsize outsize_; std::streamsize insize_; char_type* buffer_; buffer(const buffer& rhs); buffer& operator =(const buffer& rhs); public: ~buffer() MAR_NOTHROW { if (buffer_) delete[] buffer_; } explicit buffer(std::ios_base::openmode mode, std::streamsize size) : openmode_(mode), outsize_(0), insize_(0), buffer_(0) { if (mode & std::ios_base::out) outsize_ = size; std::streamsize intotal(0); if (mode & std::ios_base::in) intotal = (insize_ = size) + pbacksize; if (0 != (outsize_ + insize_)) buffer_ = new char_type[outsize_ + intotal]; } std::ios_base::openmode openmode(std::ios_base::openmode mask = std::ios_base::in | std::ios_base::out) const { return openmode_ & mask; } char_type* pbegin() const { return buffer_; } char_type* pend() const { return pbegin() + outsize_; } std::streamsize psize() const { return outsize_; } char_type* gbegin() const { return pend(); } char_type* gbase() const { return gbegin() + pbacksize; } char_type* gend() const { return gbase() + insize_; } std::streamsize gsize() const { return insize_; } }; template > class basic_streambuf : public std::basic_streambuf { typedef std::basic_streambuf base_type; public: typedef charT char_type; typedef size_t size_type; typedef ssize_t ssize_type; typedef char_traitsT char_traits_type; typedef typename char_traits_type::int_type int_type; typedef typename char_traits_type::pos_type pos_type; typedef typename char_traits_type::off_type off_type; private: typedef buffer buffer_type; archive ar_; buffer_type buffer_; state state_; struct { char_type* eback; char_type* gptr; char_type* egptr; } gsaved_; void initialise() { gsaved_.eback = gsaved_.gptr = gsaved_.egptr = buffer_.gbase(); base_type::setg(0, 0, 0); base_type::setp(0, 0); } ssize_type ioread(char_type* buf, size_type len) { ssize_type num(ar_.read(buf, len * sizeof(char_type))); if (-1 != num) num /= sizeof(char_type); return num; } ssize_type iowrite(const char_type* buf, size_type len) { if (!len) return 0; ssize_type num(ar_.write(buf, len * sizeof(char_type))); if (-1 != num) num /= sizeof(char_type); return num; } void gsave(state::which old) { if (state::get == old) { gsaved_.eback = base_type::eback(); gsaved_.gptr = base_type::gptr(); gsaved_.egptr = base_type::egptr(); } } char_type* savepback() { std::streamsize len(pbacklen()); char_type* to = buffer_.gbase() - len; char_type* from = base_type::gptr() - len; char_traits_type::copy(to, from, len); return to; } std::streamsize pbacklen() const { std::streamsize s(base_type::gptr() - base_type::eback()); return MAR_MIN(s, buffer_type::pbacksize); } int syncout() { if (state::put != state_()) return 0; char_type* begin = base_type::pbase(); std::streamsize len(base_type::pptr() - begin); std::streamsize num(iowrite(begin, len)); if (-1 == num) return -1; base_type::pbump(-len); /** * GNU sets badbit if non-zero. */ return num == len ? 0 : -1; } int syncin() { if (state::get != state_()) return 0; return 0; } bool setout() { if (state::put == state_()) return true; else if (!buffer_.openmode(std::ios_base::out)) return false; if (buffer_.openmode(std::ios_base::in)) if (-1 == syncin()) return false; gsave(state_(state::put)); base_type::setg(0, 0, 0); base_type::setp(buffer_.pbegin(), buffer_.pend()); return true; } bool setin() { if (state::get == state_()) return true; else if (!buffer_.openmode(std::ios_base::in)) return false; if (buffer_.openmode(std::ios_base::out)) if (-1 == syncout()) return false; state_(state::get); base_type::setp(0, 0); base_type::setg(gsaved_.eback, gsaved_.gptr, gsaved_.egptr); return true; } protected: int_type underflow() { if (!setin()) return char_traits_type::eof(); if (base_type::gptr() < base_type::egptr()) return char_traits_type::to_int_type(*base_type::gptr()); char_type* pback = savepback(); char_type* gbase = buffer_.gbase(); ssize_type len(ioread(gbase, buffer_.gsize())); if (-1 == len || 0 == len) return char_traits_type::eof(); setg(pback, gbase, gbase + len); return char_traits_type::to_int_type(*base_type::gptr()); } int_type overflow(int_type c = char_traits_type::eof()) { if (!setout() || -1 == syncout()) return char_traits_type::eof(); if (char_traits_type::eq_int_type(c, char_traits_type::eof())) return char_traits_type::not_eof(c); return sputc(c); } pos_type seekoff(off_type off, std::ios_base::seekdir way, std::ios_base::openmode which) { int whence; switch (way) { case std::ios_base::beg: whence = MAR_SET; break; case std::ios_base::cur: whence = MAR_CUR; break; case std::ios_base::end: whence = MAR_END; break; default: whence = 0; } return ar_.seek(off, whence); } pos_type seekpos(pos_type pos, std::ios_base::openmode which) { return ar_.seek(pos, MAR_SET); } int sync() { if (buffer_.openmode(std::ios_base::out)) if (-1 == syncout()) return -1; if (buffer_.openmode(std::ios_base::in)) if (-1 == syncin()) return -1; return 0; } std::streamsize xsgetn(char_type* s, std::streamsize n) { if (!n || !setin()) return 0; std::streamsize tot(0), len(base_type::egptr() - base_type::gptr()); /** * If any characters exist in get area then copy as required to the * output buffer. */ if (0 < len) { if (len > n) len = n; char_traits_type::copy(s, base_type::gptr(), len); s += len; tot += len; base_type::gbump(len); /** * No more characters. */ if (!(n -= len)) return tot; } /** * If number of remaining characters to read is equal to or greater * than get area then read direct to output buffer. */ if (n >= buffer_.gsize()) { if (-1 != (len = ioread(s, n))) tot += len; return tot; } /** * Force read to internal get area. */ int_type ch(base_type::uflow()); if (char_traits_type::eq_int_type(char_traits_type::eof(), ch)) return tot; *s++ = char_traits_type::to_char_type(ch); ++tot; if (!--n) return tot; /** * If any characters exist in get area then copy to output buffer. */ len = base_type::egptr() - base_type::gptr(); if (0 < len) { if (len > n) len = n; char_traits_type::copy(s, base_type::gptr(), len); s += len; tot += len; base_type::gbump(len); } return tot; } std::streamsize xsputn(const char_type* s, std::streamsize n) { if (!n || !setout()) return 0; std::streamsize tot(0), len(base_type::epptr() - base_type::pptr()); /** * If any space exists in the put area then copy from input buffer * to put area as required. */ if (0 < len) { if (len > n) len = n; char_traits_type::copy(base_type::pptr(), s, len); s += len; tot += len; base_type::pbump(len); /** * No more characters. */ if (!(n -= len)) return tot; } /** * The put area must be full; flush to output device. */ if (-1 == syncout()) return tot; /** * If the number of remaining characters exceed the size of the put * area then write direct to the output device. */ if (n >= buffer_.psize()) { if (-1 != (len = iowrite(s, n))) tot += len; return tot; } /** * Copy remaining characters to put area. */ char_traits_type::copy(base_type::pptr(), s, n); tot += n; base_type::pbump(n); return tot; } public: ~basic_streambuf() MAR_NOTHROW { if (is_open()) close(); } basic_streambuf(const handle& h, std::ios_base::openmode mode, std::streamsize size) : ar_(h), buffer_(mode, size) { initialise(); } int close() { if (!is_open()) return 0; int ret(sync()); if (buffer_.openmode(std::ios_base::out) && -1 == ar_.sync()) ret = -1; return -1 == ar_.release() ? -1 : ret; } bool is_open() const { return ar_.isopen(); } archive retain() { return ar_; } const archive& retain() const { return ar_; } }; typedef basic_streambuf streambuf; } #endif // INCLUDED_MAR_STREAMBUF_CPP