#ifndef ARABICA_CONVERT_ADAPTOR_H #define ARABICA_CONVERT_ADAPTOR_H ////////////////////////////////////////////////////// // // $Id$ // Written by Jez Higgins // Copyright 1999-2003 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 #if!(_MSC_VER < 1300) #include #endif 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 fromStreambufT; convert_bufadaptor(fromStreambufT& frombuf) : externalbuf_(frombuf) { } virtual ~convert_bufadaptor() { } 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 traitsT::state_type state_t; fromStreambufT& externalbuf_; std::vector outBuffer_; state_t outState_; std::vector inBuffer_; state_t inState_; 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 convert_bufadaptor::int_type convert_bufadaptor::overflow(convert_bufadaptor::int_type c) { if(traitsT::eq_int_type(traitsT::eof(), c)) return traitsT::not_eof(c); growOutBuffer(); sputc(traitsT::to_char_type(c)); return traitsT::not_eof(c); } // overflow template int convert_bufadaptor::sync() { return flushOut() ? 0 : -1; } // sync template convert_bufadaptor::int_type convert_bufadaptor::underflow() { if(gptr() != 0 && gptr() < egptr()) return (traitsT::to_int_type(*gptr())); if(!readIn()) return traitsT::eof(); return traitsT::to_int_type(*gptr()); } // underflow template convert_bufadaptor::int_type convert_bufadaptor::pbackfail(int_type c) { if(gptr() == eback()) return traitsT::eof(); gbump(-1); if(!traitsT::eq_int_type(c, traitsT::eof())) *(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); 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 = pptr() - &outBuffer_[0]; if(!length) return true; bool ok(true); const std::codecvt& cvt = #if!(_MSC_VER < 1300) std::use_facet >(this->getloc()); #else std::use_facet(this->getloc(), (std::codecvt*)0, true); #endif if(cvt.always_noconv()) std::copy(&outBuffer_[0], &outBuffer_[0] + length, std::ostreambuf_iterator(&externalbuf_)); else { // we must do code conversion 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, 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) setp(&outBuffer_[0], &outBuffer_[0] + outBuffer_.capacity()); return ok; } // flushOut template std::streamsize convert_bufadaptor::readIn() { if(!inBuffer_.capacity()) growInBuffer(); #if!(_MSC_VER < 1300) size_t pbCount = std::min(gptr() - eback(), pbSize_); #else size_t pbCount = min(gptr() - eback(), pbSize_); #endif memcpy(&(inBuffer_[0]) + (pbSize_-pbCount)*sizeof(charT), gptr() - pbCount*sizeof(charT), pbCount*sizeof(charT)); const std::codecvt& cvt = #if!(_MSC_VER < 1300) std::use_facet >(this->getloc()); #else std::use_facet(this->getloc(), (std::codecvt*)0, true); #endif std::vector from(inBuffer_.capacity()); std::streamsize res = externalbuf_.sgetn(&(from[0]), static_cast(from.capacity())); if(res > 0) { std::codecvt_base::result r; do { const externalCharT* from_next; charT* to_next; r = cvt.in(inState_, &(from[0]), &(from[0]) + res, from_next, &(inBuffer_[0])+pbSize_, &(inBuffer_[0]) + inBuffer_.capacity() - pbSize_, to_next); if(r == std::codecvt_base::noconv) memcpy(&(inBuffer_[0])+pbSize_, &from[0], res); else res = static_cast(to_next - (&(inBuffer_[0])+pbSize_)); if(r == std::codecvt_base::partial) growInBuffer(); } 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) } setg(&(inBuffer_[0]) + (pbSize_-pbCount), &(inBuffer_[0])+pbSize_, &(inBuffer_[0])+pbSize_+res); return static_cast(res); } // readIn //////////////////////////////////////////////////// // iconvert_adaptor template, typename fromCharT = charT, typename fromTraitsT = std::char_traits > class iconvert_adaptor : public std::basic_istream { typedef std::basic_istream fromStreamT; public: explicit iconvert_adaptor(fromStreamT& fromstream) : std::basic_istream(&bufadaptor_), bufadaptor_(*(fromstream.rdbuf())) { } // iconvert_adaptor virtual ~iconvert_adaptor() { } convert_bufadaptor* rdbuf() const { return const_cast*>(&bufadaptor_); } // rdbuf private: convert_bufadaptor bufadaptor_; }; // class iconvert_adaptor #endif