#ifndef ARABICA_CONVERT_ADAPTOR_H #define ARABICA_CONVERT_ADAPTOR_H ////////////////////////////////////////////////////// // // Written by Jez Higgins // Copyright 1999-2010 Jez UK Ltd, http://www.jezuk.co.uk/ // // convert_adaptor is a stream that wraps around another stream. This // may not seem like a big deal, but convert_adaptor applies codecvt // facets along the way. This allows you to apply encryption or // decryption (or any other transcoding) transparently. It can // also adapt wchar_t streams to char streams, or vice versa, allowing // you to write std::wstrings out as UTF-8 chars to a file, for instance. ////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include namespace Arabica { namespace io { template, typename externalCharT = charT, typename externalTraitsT = std::char_traits > class convert_bufadaptor : public std::basic_streambuf { public: typedef typename traitsT::int_type int_type; typedef std::basic_streambuf externalStreambufT; typedef std::basic_streambuf streambufT; explicit convert_bufadaptor(externalStreambufT& externalbuf) : externalbuf_(&externalbuf), inEof_(false) { } virtual ~convert_bufadaptor() { } void set_buffer(externalStreambufT& externalbuf) { externalbuf_ = &externalbuf; inEof_ = false; } protected: virtual int_type overflow(int_type c = traitsT::eof()); virtual int sync(); virtual int_type underflow(); virtual int_type pbackfail(int_type c); private: typedef typename externalTraitsT::int_type external_int_type; typedef typename traitsT::state_type state_t; externalStreambufT* externalbuf_; std::vector outBuffer_; state_t outState_; std::vector inBuffer_; state_t inState_; bool inEof_; void growOutBuffer(); bool flushOut(); void growInBuffer(); std::streamsize readIn(); static const std::streamsize bufferSize_; static const std::streamsize pbSize_; }; // convert_bufadaptor template const std::streamsize convert_bufadaptor::bufferSize_ = 1024; template const std::streamsize convert_bufadaptor::pbSize_ = 4; // why 4? both Josuttis and Langer&Kreft use 4. template typename convert_bufadaptor::int_type convert_bufadaptor::overflow(int_type c) { if(traitsT::eq_int_type(traitsT::eof(), c)) return traitsT::not_eof(c); growOutBuffer(); streambufT::sputc(traitsT::to_char_type(c)); return traitsT::not_eof(c); } // overflow template int convert_bufadaptor::sync() { return flushOut() ? 0 : -1; } // sync template typename convert_bufadaptor::int_type convert_bufadaptor::underflow() { if(streambufT::gptr() != 0 && streambufT::gptr() < streambufT::egptr()) return (traitsT::to_int_type(*streambufT::gptr())); if(!readIn()) return traitsT::eof(); return traitsT::to_int_type(*streambufT::gptr()); } // underflow template typename convert_bufadaptor::int_type convert_bufadaptor::pbackfail(int_type c) { if(streambufT::gptr() == streambufT::eback()) return traitsT::eof(); streambufT::gbump(-1); if(!traitsT::eq_int_type(c, traitsT::eof())) *(streambufT::gptr()) = traitsT::to_char_type(c); return traitsT::not_eof(c); } // pbackfail template void convert_bufadaptor::growOutBuffer() { size_t oldsize = outBuffer_.capacity(); size_t newsize = (oldsize ? oldsize*2 : bufferSize_); outBuffer_.resize(newsize); streambufT::setp(&outBuffer_[0] + oldsize, &outBuffer_[0] + outBuffer_.capacity()); } // growOutBuffer template void convert_bufadaptor::growInBuffer() { size_t oldsize = inBuffer_.capacity(); size_t newsize = (oldsize ? oldsize*2 : bufferSize_) + pbSize_; inBuffer_.resize(newsize); } // growInBuffer template bool convert_bufadaptor::flushOut() { size_t length = streambufT::pptr() - &outBuffer_[0]; if(!length) return true; bool ok(true); const std::codecvt& cvt = std::use_facet >(this->getloc()); if(cvt.always_noconv()) std::copy(&outBuffer_[0], &outBuffer_[0] + length, std::ostreambuf_iterator(externalbuf_)); else { // we must do code conversion length += cvt.max_length(); // add a little std::vector to(length); const charT* from_next = &(outBuffer_[0]); std::codecvt_base::result r; do { externalCharT* to_next; r = cvt.out(outState_, from_next, streambufT::pptr(), from_next, &to[0], &to[0]+length, to_next); if(r == std::codecvt_base::noconv) { std::copy(&outBuffer_[0], &outBuffer_[0] + length, std::ostreambuf_iterator(externalbuf_)); break; } std::copy(&to[0], to_next, std::ostreambuf_iterator(externalbuf_)); } while(r == std::codecvt_base::partial); ok = ok ? (r != std::codecvt_base::error) : false; } // if(cvt.always_noconv()) if(ok) streambufT::setp(&outBuffer_[0], &outBuffer_[0] + outBuffer_.capacity()); return ok; } // flushOut template std::streamsize convert_bufadaptor::readIn() { if(!inBuffer_.capacity()) growInBuffer(); size_t pbCount = std::min(streambufT::gptr() - streambufT::eback(), pbSize_); memcpy(&(inBuffer_[0]) + (pbSize_-pbCount)*sizeof(charT), streambufT::gptr() - pbCount*sizeof(charT), pbCount*sizeof(charT)); const std::codecvt& cvt = std::use_facet >(this->getloc()); externalCharT from[1024]; std::streamsize res = 0; if(!inEof_) { external_int_type ec = externalbuf_->sgetc(); while((!externalTraitsT::eq_int_type(externalTraitsT::eof(), ec)) && (res != bufferSize_)) { from[res++] = static_cast(ec); ec = externalbuf_->snextc(); } inEof_ = (externalTraitsT::eq_int_type(externalTraitsT::eof(), ec)); } // if ... std::streamsize converted = 0; if(res > 0) { std::codecvt_base::result r; const externalCharT* from_next = from; do { // can't cache this as may reallocate buffer charT* const to = &(inBuffer_[0])+pbSize_; charT* const to_end = &(inBuffer_[0]) + inBuffer_.capacity() - pbSize_; charT* to_next; externalCharT* const from_end = from + res; r = cvt.in(inState_, from_next, from_end, from_next, to + converted, to_end, to_next); if(r == std::codecvt_base::noconv) { memcpy(&(inBuffer_[0])+pbSize_, from, res); converted = res; } else converted += static_cast(to_next - (to + converted)); if(r == std::codecvt_base::partial) { // haven't done everything if(to_next == to_end) growInBuffer(); // need more room! else { int shortfall = (from + res) - from_next; memcpy(from, from_next, shortfall); res = externalbuf_->sgetn(from + shortfall, static_cast(bufferSize_ - shortfall)); from_next = &from[0]; if(res == 0) // oh dear break; // let's bail! res += shortfall; } } // if(r == std::codecvt_base::partial) } while(r == std::codecvt_base::partial); if(r == std::codecvt_base::error) { // couldn't convert - let's bail return 0; } // if(r == std::codecvt_base::error) } streambufT::setg(&(inBuffer_[0]) + (pbSize_-pbCount), &(inBuffer_[0])+pbSize_, &(inBuffer_[0])+pbSize_+converted); return static_cast(res); } // readIn //////////////////////////////////////////////////// // iconvert_adaptor template class convert_adaptor_buffer { protected: typedef std::basic_streambuf externalStreambufT; explicit convert_adaptor_buffer(externalStreambufT& externalbuf) : bufadaptor_(externalbuf) { } convert_bufadaptor bufadaptor_; }; // convert_adaptor_buffer template, typename fromCharT = charT, typename fromTraitsT = std::char_traits > class iconvert_adaptor : private convert_adaptor_buffer, public std::basic_istream { typedef std::basic_istream baseStreamT; typedef std::basic_istream fromStreamT; typedef convert_adaptor_buffer baseBufT; protected: using baseBufT::bufadaptor_; public: explicit iconvert_adaptor(fromStreamT& fromstream) : baseBufT(*(fromstream.rdbuf())), baseStreamT(&bufadaptor_) { } // iconvert_adaptor virtual ~iconvert_adaptor() { } baseBufT* rdbuf() const { return const_cast(&bufadaptor_); } // rdbuf void set_stream(fromStreamT& fromStream) { bufadaptor_.set_buffer(*fromStream.rdbuf()); } }; // class iconvert_adaptor //////////////////////////////////////////////////////// // oconvert_adaptor template, typename toCharT = charT, typename toTraitsT = std::char_traits > class oconvert_adaptor : private convert_adaptor_buffer, public std::basic_ostream { typedef std::basic_ostream baseStreamT; typedef std::basic_ostream toStreamT; typedef convert_adaptor_buffer baseBufT; protected: using baseBufT::bufadaptor_; public: explicit oconvert_adaptor(toStreamT &toStream) : baseBufT(*(toStream.rdbuf())), baseStreamT(&bufadaptor_) { } // oconvert_adaptor virtual ~oconvert_adaptor() { baseStreamT::flush(); } // ~oconvert_adaptor convert_bufadaptor* rdbuf() const { return const_cast*>(&bufadaptor_); } // rdbuf void set_stream(toStreamT& toStream) { bufadaptor_.set_buffer(*toStream.rdbuf()); } }; // class oconvert_adaptor } // namespace io } // namespace Arabica #endif